6 - Преместване, завъртане

Промяна името на променлива

В „GlobalVars“ в името на променливата „lastHeiht“ има грешка. За да я поправим щракаме върху нея (или в навигатора) с десен бутон и избираме „Refactor“ > „Rename“.

rename-refactor

NetBeans намира всички употреби на променливата в кода и ги заменя с новото име. По този начин избягваме проблемите, които могат да настъпят при ръчната промяна на променливата.

Преместване

Добавяме нов бутон с име „jToggleButtonMove“.

buton-premestvane

При настъпване на събитието „влачене“ на мишката ще запомняме координати ѝ, за да ги използваме при настъпването на следващото влачене. По този начин ще определяме накъде и колко трябва да се премести избраната фигура. За целта в „GlobalVars“ създаваме променливата „moveLastPoint“.

koordinati-premestvane

При натискане на мишката (в „jPanelCanvasMousePressed“) вземаме точката, в която това е станало.

tochka-natiskane

В „jPanelCanvasMouseDragged“ ако бутонът „Преместване“ е натиснат обхождаме всички фигури и чрез метода „translate“ променяме местоположението на тези, които са избрани. Този метод приема като параметри преместването по оста x и по оста y. Изчисляваме го, като от текущите координати извадим старите. Накрая запомняме текущите, за да ги използваме при следващото извикване на „jPanelCanvasMouseDragged“, а това става при всяко преместване на мишката с натиснат бутон.

premestvane-kod

Забележете, че трябва да преобразуваме „primitive“ до „Rectangle“, за да използваме метода „translate“. В нашия интерфейс „Primitive“ няма такъв метод.

Така реализирано преместването не изисква да сме щракнали върху дадена фигурата, за да я преместим. Достатъчно е да влачим мишката при натиснат бутон „Преместване“ и избраните фигури ще се преместват. Това поведение може да бъде сметнато за неправилно от някои. Според мен то е полезно свойство на програмата ни.

Завъртане

Стигаме до може би най-трудната част от нашата програма до сега - завъртането. То отнема повече време за направа, примерно 2-3 дни.

Има немалко промени по кода. Те няма да бъдат описани последователно, а по класове.

Единственият клас, в който няма промени е „DrawTask“.

В интерфейса „Primitive“ има три нови метода.

interfeis-zavurtane

„getAngleOfRotation“ връща ъгъла на завъртане на дадената фигура. „setAngleOfRotation“ го променя, а „addToAngleOfRotation“ добавя параметъра „angleOfRotation“ към текущия ъгъл на завъртане (събират се големините на ъглите).

С реализацията (имплементацията) на методите ще се запознаем по-късно.

Както знаем в метода „draw“ на „MyRectangle“ с „fillRect“ рисуваме фигурата. Освен това извършваме някои други операции, които се повтарят за всички фигури. Ще изнесем тези операции в два отделни метода „doPreDraw“ и „doPostDraw“ в родителския клас „CommonPrimitive“. Първия ще извикваме преди самото рисуване, а втория след него.

predi-sled-risuvane

С кода на тези методи ще се запознаем след малко.

По същия начин променяме метода „draw“ и в „MyEllipse“.

В „CommonPrimitive“ добавяме полето „angleOfRotation“, в което ще помним в градуси големината на ъгъла на завъртана на обекта.

angleOfRotation

Мисля, че методът „getAngleOfRotation“ няма нужда от обяснение.
„setAngleOfRotation“ и „addToAngleOfRotation“ са идентични с единствената разлика, че във втория добавяме ъгъла параметър към текущия. И в двата метода, преди да запазим ъгъла го преобразуват чрез метода „calculateAngle“. Той „нормализира“ ъгъла така, че винаги да е в диапазона -90 до 90 градуса. За целите на програмата ъгъл на завъртане от примерно 100 градуса е еквивалентен на ъгъл на завъртане от -80 градуса. Това „нормализиране“ ще ни предпази от проблеми по-нататък.

Надявам се, че всички от вас са чували за оператора деление с остатък - %.

kod-ugul-zavurtane

В методите „doPreDraw“ и „doPostDraw“ се извършват, както старите операции от метода „draw“, така и самото завъртане. То е реализирано по следния начин:

завърта се платното за рисуване на x градуса;
рисува се фигурата;
платното се връща обратно, като се завърта на -x градуса.

Завъртането се осъществява с метода „rotate“, който приема като параметри ъгъл в радиани, и координати на точка - център на въртенето. Тъй като нашия ъгъл е в градуси чрез „Math.toRadians“ го преобразуваме в радиани. Методите „getCenterX“ и „getCenterY“ са наследени от класа „Rectangle“ и връщат координатите на центъра на фигурата. „rotate“ е от класа „Graphics2D“ затова „graphics“ се преобразува към него .

Рамката около избрана фигура също трябва да бъде завъртяна затова извикваме „drawBorder“ преди да върнем на обратно платното за рисуване.

predi-sled-risuvane-kod

Във формата за класа „GUI“ добавяме надпис „Завъртане“ и плъзгач, чрез който ще завъртаме фигурите.

pluzgach-zavurtane

Променяме името му на „jSliderRotation“ и полетата „maximum“, „minimum“ и „value“ в настройките му. Те означават съответно максимална, минимална и текуща стойност.

pluzgach-nastroiki

Пренасяме се в кода на класа „GUI“.

Ще искаме да променяме стойността на плъзгача, когато изберем някоя фигура. За целта го добавяме в глобалните променливи, както направихме с „jPanelObjectColor“.

globalen-pluzgach

При преместване на плъзгача ще вземаме негова текуща позиция и от нея ще изваждаме предишната. Така ще намираме разстоянието, на което се е преместил и в коя посока. Ще помним предишната позиция в глобалната променлива „sliderLastPosition“. Това е направено в метода „jSliderRotationStateChanged“, който обработва събитието „преместване на плъзгача“. В него се обхождат всички фигури и се променя ъгълът на завъртане на тези, които са избрани.
Променя се надписът „Завъртане“ да показва колко градуса е ъгълът на завъртане и се рисуват фигурите.

pluzgane

Искаме когато дадена фигура бъде избрана плъзгачът да показва нейното текущо завъртане.

Когато се създаде нова фигура тя става избрана и завъртането ѝ е 0 градуса. Затова в „jPanelCanvasMouseReleased“ трябва да преместваме плъзгача по средата и да изчистваме последната стойност - „sliderLastPosition“.

pluzgach-zanuliavane

Ако забелязвате направили сме промени и при рисуването на фигурите най-долу в метода. Целта е да се избегне оцветяването на части от очертанието на панела за рисуване. Той се прерисува с „repaint“, след което със забавяне се рисуват и фигурите.

Това е направено и в метода „jSliderRotationMouseReleased“, който обработва събитието „отпускане на мишката“ от плъзгача.

В метода „jSliderRotationMousePressed“ се поставя фокуса върху платното за рисуване, както правехме при натискане на бутона за избиране. Целта на това е да могат да се избират фигури след извършено завъртане без да се налага да се натиска отново бутона „Избиране“.

pluzgach-subitiia

Преминаваме към последния клас - „GlobalVars“.

Избиране на завъртяна фигура

Един от проблемите при завъртането е, че при избирането на завъртяната фигура не можем просто да проверяваме дали сме щракнали в нейната област. Трябва първо да завъртим областта и тогава да направим проверката. Следва описание как това е направено.

Методът „getPathIterator“ от класа „Rectangle2D“ връща описание на границите на правоъгълна област. Един вид рамка на областта, в която се съдържа фигурата. Това описание („PathIterator“) можем да използваме, като параметър на статичния метод „contains“ от класа „Path2D“. Този метод проверява дали вторият му параметър, който е от тип „Rectangle2D“ се съдържа в първия - от тип „PathIterator“.

До тук не сме постигнали нещо повече от стария метод „contains“ от „Rectangle2D“, който ползвахме. Хубавото обаче е, че методът „getPathIterator“ приема като параметър афинно преобразувание („AffineTransform“), чрез което можем да завъртим връщаните от „getPathIterator“ граници на област. Статичният метод „getRotateInstance“ приема като параметри, ъгъл на завъртане и координати на центъра на завъртане и връща афинно преобразувание.

Ето как изглежда казаното като код.

AffineTransform-PathIterator

„primitiveRectangle2D“ е фигурата, която проверяваме дали е избрана, преобразувана (cast-ната) до тип „Rectangle2D“.
„rectangle2D“ е областта заградена с мишката.
„toBeSelected“ е помощна променлива.

Всичко това извършваме в метода „select“ при заградена област от мишката.
Не забравяме ако фигурата е избрана да променим позицията на плъзгача и запазим последната такава.

proverka-oblast

Кодът за проверката при щракване с мишката е почти същият, само че сега ползваме метода „contains“ от „Path2D“, който приема като параметри описание на границите на правоъгълна област и точка и проверява дали тази точката се съдържа в областта. Затова като първи параметър подаваме описанието на областта, в която се съдържа нашата фигура.

proverka-tochka

Този подход за избиране на завъртяни фигури не работи по най-добрия възможен начин, но все пак върши работа за нашата програма.

Ако се чувствате объркани от последните редове, не се притеснявайте. Прочетете ги бавно още един или два пъти. Самият аз не съм запознат с подробности за работата и използването на тези класове. В много случаи просто предполагам дадено поведение, тествам и оставам приятно изненадан, че нещата работят. Но не се заблуждавайте, този подход работи само, когато искате бързо да направите програма, която ще използвате само веднъж и ще я захвърлите. Ако пишете сериозно приложение такъв начин на програмиране ще ви донесе много главоболия по-нататък.

Нека отново да напомня, че този код може да навреди на вашето развитие, като софтуерни разработчици!

И така, ето изглед от програмата ни, с която вече доста по-лесно се правят по-сложни изображения.

izgled-6

Кодът до сега - Trayangle-6.zip

Програмата - Trayangle-6.jar

Коментари

Триъгълник ? Това от къде се

Триъгълник ? Това от къде се появи, като нямаш такъв премитив?

И обхващащия правоъгълник е сменен на триъгълник.

Това го няма в кода :)

Всичко е в кода :)

Това не е триъгълник а квадрат върху, който има нарисувани и завъртяни други два правоъгълника с бял цвят. Рамката, която се вижда също е от тях.

Използвайте фантазията си!

Панела за рисуване

Здравй много добри уроци доста години след като си ги направил все още ги ползваме. Попаднах на един проблем Когато рисувам примерно квадрат в полето за рисуване квадрата изчезва някъде на заден план и като почна да рисувам следващия го виждам защо се е получило така ?

Публикувай нов коментар

  • Адресите на уеб-страници и e-mail адресите автоматично се конвертират в хипервръзки.
  • Разрешени HTML tag-ове: <a> <p> <span> <div> <h1> <h2> <h3> <h4> <h5> <h6> <img> <map> <area> <hr> <br> <br /> <ul> <ol> <li> <dl> <dt> <dd> <table> <tr> <td> <em> <b> <u> <i> <strong><font> <del> <ins> <sub> <sup> <quote> <blockquote> <pre> <address> <code> <cite> <embed> <object> <param> <strike> <caption>
  • Линиите и параграфите се прекъсват автоматично.
  • Mark language-dependent sections with == lc == where lc (or lc-xx) is a language code, other or all.

Повече информация за опциите на форматиране

CAPTCHA
6 + 3 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.