$$ \newcommand{\floor}[1]{\left\lfloor{#1}\right\rfloor} \newcommand{\ceil}[1]{\left\lceil{#1}\right\rceil} \renewcommand{\mod}{\,\mathrm{mod}\,} \renewcommand{\div}{\,\mathrm{div}\,} \newcommand{\metar}{\,\mathrm{m}} \newcommand{\cm}{\,\mathrm{cm}} \newcommand{\dm}{\,\mathrm{dm}} \newcommand{\litar}{\,\mathrm{l}} \newcommand{\km}{\,\mathrm{km}} \newcommand{\s}{\,\mathrm{s}} \newcommand{\h}{\,\mathrm{h}} \newcommand{\minut}{\,\mathrm{min}} \newcommand{\kmh}{\,\mathrm{\frac{km}{h}}} \newcommand{\ms}{\,\mathrm{\frac{m}{s}}} \newcommand{\mss}{\,\mathrm{\frac{m}{s^2}}} \newcommand{\mmin}{\,\mathrm{\frac{m}{min}}} \newcommand{\smin}{\,\mathrm{\frac{s}{min}}} $$

Prijavi problem


Obeleži sve kategorije koje odgovaraju problemu

Još detalja - opišite nam problem


Uspešno ste prijavili problem!
Status problema i sve dodatne informacije možete pratiti klikom na link.
Nažalost nismo trenutno u mogućnosti da obradimo vaš zahtev.
Molimo vas da pokušate kasnije.

Померање цртежа

До сада смо видели како можемо да стварамо цртеже састављене од основних облика. При томе је за сваки од тих облика било потребно одредити тачан положај да би се сви делови уклопили у целину. За неке цртеже је било потребно да се координате појединих тачака израчунају на основу познатих координата других тачака или неких величина на слици.

Видели смо да када рачунање координата уврстимо у сам програм који и црта слику, такав програм постаје удобнији за експериментисање са сликом и погоднији за преправљање и генерисање нових, сличних слика.

Још један разлог да уврстимо рачунање координата директно у програм је што нам то, уз мало датне организације омогућава да исту или врло сличну слику врло једноставно нацртамо више пута на екрану. Пре него што дођемо до детаља овог поступка, било би добро да проверите потребно предзнање и одговорите на ова питања:

    Q-14: Kоје су координате тачке која се налази 10 пиксела лево од тачке (50, 70)?

  • (50, 60)
  • Покушајте поново
  • (50, 80)
  • Покушајте поново
  • (40, 70)
  • Тачно
  • (60, 70)
  • Покушајте поново
  • (40, 60)
  • Покушајте поново

    Q-15: Kоје су координате тачке која се налази 10 пиксела испод тачке (50, 70)?

  • (50, 60)
  • Покушајте поново
  • (50, 80)
  • Тачно
  • (40, 70)
  • Покушајте поново
  • (60, 70)
  • Покушајте поново
  • (40, 60)
  • Покушајте поново

    Q-16: Правоугаоник је нацртан помоћу g.DrawRectangle(olovka, 100, 150, 80, 90). Како се може нацртати правоугаоник исте величине који се налази 30 пиксела лево и 30 пиксела изнад овог правоугаоника?

  • g.DrawRectangle(olovka, 70, 120, 50, 60))
  • Покушајте поново
  • g.DrawRectangle(olovka, 100, 150, 110, 120))
  • Покушајте поново
  • g.DrawRectangle(olovka, 100, 150, 50, 60))
  • Покушајте поново
  • g.DrawRectangle(olovka, 70, 120, 80, 90))
  • Тачно
  • g.DrawRectangle(olovka, 70, 180, 80, 90))
  • Покушајте поново

    Q-17: Круг је нацртан помоћу наредбе g.FillEllipse(cetka, x, y, a, a); Како се може нацртати круг исте боје и величине, који додирује дати круг одоздо?

  • g.FillEllipse(cetka, x, y-a, a, a);
  • Покушајте поново
  • g.FillEllipse(cetka, x, y-2*a, a, a);
  • Покушајте поново
  • g.FillEllipse(cetka, x, y+a, a, a);
  • Тачно
  • g.FillEllipse(cetka, x, y+2*a, a, a);
  • Покушајте поново

Преправљање непомичног цртежа у помични

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_movable_cloud_fixed.png

Овакву слику можемо да нацртамо на пример помоћу следеће функције.

private void Form1_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    Brush zutaCetka = new SolidBrush(Color.Yellow);
    Brush belaCetka = new SolidBrush(Color.White);

    g.FillEllipse(zutaCetka, 20, 50, 160, 160); // crtamo sunce

    // crtamo oblak od tri kruga
    g.FillEllipse(belaCetka, 150, 150, 100, 100);
    g.FillEllipse(belaCetka, 120, 170, 60, 60);
    g.FillEllipse(belaCetka, 220, 170, 60, 60);
}

Облак смо представили помоћу три круга, једног већег у средини и два мања око њега:

g.FillEllipse(belaCetka, 150, 150, 100, 100);
g.FillEllipse(belaCetka, 120, 170, 60, 60);
g.FillEllipse(belaCetka, 220, 170, 60, 60);

Шта треба да урадимо да бисмо исти такав облак нацртали на другом месту, на пример 30 пиксела изнад и 80 пиксела десно? Ако сте тачно одговорили на претходна питања, неће вам бити превише тешко да закључите да се облак померен на тражени начин може добити помоћу следеће три наредбе:

g.FillEllipse(belaCetka, 230, 120, 100, 100);
g.FillEllipse(belaCetka, 200, 140, 60, 60);
g.FillEllipse(belaCetka, 300, 140, 60, 60);

Параметре ових наредби смо, наравно, добили додавањем 80 на све \(x\) координате и одузимањем 30 од свих \(y\) координата. Овај начин померања цртежа је прихватљив ако хоћемо да нацртамо само један нови облак и већ имамо коначну одлуку о томе где ћемо тај облак да нацртамо. Међутим, када хоћемо да нацртамо неколико облака и да при томе експериментишемо са њиховим распоредом, овај начин померања није најудобнији. Како да прилагодимо цртање облака, да би испробавање разних распореда облака било што једноставније?

Један удобан начин је да напишемо функцију која црта један облак. При томе желимо да функцији прослеђујемо што мање координата да бисмо имали што мање посла при позивању функције и да би нам експериментисање било што удобније. Ако желимо да облак слободно померамо, функцији треба проследити најмање две координате, које ће на неки начин да задају положај облака. Рецимо да смо одлучили да функцији прослеђујемо координате центра облака \((x_c, y_c)\), око којег тај облак треба да буде нацртан (види слику).

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_movable_cloud.PNG

Са слике видимо и да је

\(x = x_c - r\),

\(x_1 = x_c - r - r_1\),

\(x_2 = x_c + r - r_1\).

Када уврстимо \(r = 50\) и \(r_1 = 30\), добијамо

\(x = x_c - 50\),

\(x_1 = x_c - 80\),

\(x_2 = x_c + 20\).

Слично томе, за \(y\) координате имамо

\(y = y_c - r\),

\(y_1 = y_2 = y_c - r_1\),

а после уврштавања \(r = 50\) и \(r_1 = 30\) добијамо

\(y = y_c - 50\),

\(y_1 = y_2 = y_c - 30\)

Функцију за цртање облака сада можемо да напишемо овако:

private void Oblak(Graphics g, int xc, int yc, int siva)
{
    Color boja = Color.FromArgb(siva, siva, siva);
    Brush cetka = new SolidBrush(boja);
    // crtamo oblak od tri kruga
    g.FillEllipse(cetka, xc - 50, yc - 50, 100, 100);
    g.FillEllipse(cetka, xc - 80, yc - 30, 60, 60);
    g.FillEllipse(cetka, xc + 20, yc - 30, 60, 60);
}

Овде смо успут омогућили и задавање боје, то јест сиве нијансе облака.

Писање ове функције је изискивало нешто труда, али када је једном напишемо, експериментисање са распоредом (и нијансама) облака постаје веома једноставно - довољно је из функције Form1_Paint позивати функцију Oblak, задајући центре и боје тих облака по жељи:

private void Form1_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    Brush zutaCetka = new SolidBrush(Color.Yellow);

    g.FillEllipse(zutaCetka, 20, 50, 160, 160); // crtamo sunce

    Oblak(g, 240, 200, 180);
    Oblak(g, 270, 250, 210);
    Oblak(g, 230, 100, 230);
    Oblak(g, 80, 80, 190);
    Oblak(g, 110, 320, 255);
}
https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_movable_clouds.png

Размислите како би изгледао програм који црта исту ову слику без функције Oblak. Број наредби би се брзо умножавао, што би програм чинило мање прегледним. Осим тога, било би пртребно више преправљања за сваку нову слику, па би рад био мање удобан. Због свега тога и могућност грешке би била већа, а шансе да искуство програмирања буде пријатно би биле мање.


Резимирајмо, уз мала уопштења, шта је потребно да се уради да бисмо могли да прказујемо један цртеж на разним местима:

  • Треба да изаберемо једну тачку на цртежу чије се координате задају директно. Ову изабрану тачку зваћемо главна тачка, (понекад се ова тачка назива и сидро, енгл. anchor). У примеру облака, главна тачка је центар средњег круга.

  • Након избора главне тачке, координате свих осталих битних тачака одређујемо у односу на њу, тако што на координате главне тачке додајемо или одузимамо одређени померај.

У општем случају, на цртежу може бити и других облика осим кругова. Тачке које одређују положаје тих облика су:

  • за дуж: њени крајеви

  • за многоугао: његова темена

  • за правоугаоник: његово горње лево теме

  • за елипсу: горње лево теме правоугаоника описаног око те елипсе

  • за круг: горње лево теме павоугаоника (квадрата) описаног око тог круга

Све ове тачке треба задати у односу на главну тачку, то јест изразити њихове координате као координате главне тачке увећане или умањене за неку вредност. При померању цртежа (за сада) не мењамо његову величину, па зато нећемо мењати ширину и висину правоугаоника, елипсе и круга.

Проверите колико сте разумели претходна објашњења и одгоровите на питања.

    Q-18: Желимо да прилагодимо цртеж који се састоји од неколико основних облика, тако да се све црта у односу на сидро са координатама x=100, y=100. Једна од наредби које формирају цртеж је

    g.FillEllipse(cetka, 120, 90, 100, 60);
    

    Која наредба треба да замени дату?

  • g.FillEllipse(cetka, x, y, 100, 60);
  • Покушајте поново
  • g.FillEllipse(cetka, x+120, y+90, 100, 60);
  • Покушајте поново
  • g.FillEllipse(cetka, x+20, y-10, 100, 60);
  • Тачно
  • g.FillEllipse(cetka, x-20, y+10, 100, 60);
  • Покушајте поново

    Q-19: Желимо да прилагодимо цртеж који се састоји од неколико osnovnih облика, тако да се све црта у односу на сидро са координатама x=100, y=100. Једна од наредби које формирају цртеж је

    g.DrawLine(olovka, 50, 50, 150, 150);
    

    Која наредба треба да замени дату?

  • g.DrawLine(olovka, x-50, y-50, 150, 150);
  • Покушајте поново
  • g.DrawLine(olovka, x-50, y-50, x+50, y+50);
  • Тачно
  • g.DrawLine(olovka, x-50, x+50, y-50, y+50);
  • Покушајте поново
  • g.DrawLine(olovka, x+50, y+50, x+150, y+150);
  • Покушајте поново

    Q-20: Желимо да прилагодимо цртеж који се састоји од неколико облика, тако да се све црта у односу на сидро са координатама x=100, y=100. Једна од наредби које формирају цртеж је

    g.FillRectangle(cetka, 50, 50, 100, 100);
    

    Која наредба треба да замени дату?

  • g.FillRectangle(cetka, x-50, y-50, x, y);
  • Покушајте поново
  • g.FillRectangle(cetka, x, y, 100, 100);
  • Покушајте поново
  • g.FillRectangle(cetka, x+50, y+50, x+100, y+100);
  • Покушајте поново
  • g.FillRectangle(cetka, x-50, y-50, 100, 100);
  • Тачно

    Q-21: Желимо да померимо цртеж који се састоји од неколико облика надесно за 100 пиксела. Означите тачна тврђења.

  • Уместо g.DrawLine(olovka, a, b, c, d); позваћемо g.DrawLine(olovka, a+100, b, c, d);
  • Покушајте поново
  • Уместо g.FillEllipse(cetka, a, b, c, d); позваћемо g.FillEllipse(cetka, a-100, b-100, c, d);
  • Покушајте поново
  • Уместо g.FillRectangle(cetka, a, b, c, d); позваћемо g.FillRectangle(cetka, a+100, b, c+100, d);
  • Покушајте поново
  • Уместо g.FillRectangle(cetka, a, b, c, d); позваћемо g.FillRectangle(cetka, a+100, b, c, d);
  • Тачно
  • Уместо g.FillRectangle(cetka, a, b, c, d); позваћемо g.FillRectangle(cetka, a-100, b, c, d);
  • Покушајте поново

Следи још један пример претварања фиксног цртежа у помични.

Пример - положај меде

Дат је следећи код, који исцртава главу медведића играчке:

private void UokvirenKrug(Graphics g, Color boja, int cx, int cy, int r)
{
    g.FillEllipse(new SolidBrush(boja), cx - r, cy - r, 2 * r, 2 * r);
    g.DrawEllipse(new Pen(Color.Black), cx - r, cy - r, 2 * r, 2 * r);
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    UokvirenKrug(g, Color.Yellow, 190, 80, 45); // levo uvo
    UokvirenKrug(g, Color.Yellow, 310, 80, 45); // desno uvo
    UokvirenKrug(g, Color.Yellow, 250, 150, 100); // glava
    UokvirenKrug(g, Color.Yellow, 250, 200, 50); // njuska
    UokvirenKrug(g, Color.Black, 200, 120, 15); // levo oko
    UokvirenKrug(g, Color.Black, 300, 120, 15); // desno oko
    UokvirenKrug(g, Color.Black, 250, 170, 15); // vrh njuske
}
https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_movable_bear.png

У програму се седам пута позива функција UokvirenKrug, која задати круг уоквирује црном бојом (мада је за три мала црна круга могла да буде позвана и само функција FillEllipse). Да бисмо могли да мењамо положај цртежа, изаберимо главну тачку (сидро). Нека то буде центар великог круга, то јест главе медведића. Координате ове тачке су (250, 150). Сада је потребно да координате центара свих осталих кругова изразимо полазећи од главне тачке, померајући се за потребан број пиксела у смеру \(x\) и \(y\) осе. Узмимо као пример десно уво медведића.

\(x\) координата центра десног увета је \(310 = 250 + 60\), док је \(y\) координата \(80 = 150 - 70\). Одавде се види да координате центра десног увета можемо у програму да напишемо као (cx + 60,  cy - 70), где су (cx, cy) координате главне тачке. Спровођењем истог поступка за остале кругове добијамо функцију CrtajMedu:

private void UokvirenKrug(Graphics g, Color boja, int cx, int cy, int r)
{
    g.FillEllipse(new SolidBrush(boja), cx - r, cy - r, 2 * r, 2 * r);
    g.DrawEllipse(new Pen(Color.Black), cx - r, cy - r, 2 * r, 2 * r);
}

private void CrtajMedu(Graphics g, int cx, int cy)
{
    UokvirenKrug(g, Color.Yellow, cx - 60, cy - 70, 45); // levo uvo
    UokvirenKrug(g, Color.Yellow, cx + 60, cy - 70, 45); // desno uvo
    UokvirenKrug(g, Color.Yellow, cx, cy, 100); // glava
    UokvirenKrug(g, Color.Yellow, cx, cy + 50, 50); // njuska
    UokvirenKrug(g, Color.Black, cx - 50, cy - 30, 15); // levo oko
    UokvirenKrug(g, Color.Black, cx + 50, cy - 30, 15); // desno oko
    UokvirenKrug(g, Color.Black, cx, cy + 20, 15); // vrh njuske
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
    CrtajMedu(e.Graphics, ClientSize.Width / 2, ClientSize.Height / 2);
}

Овако написан програм нам омогућава да једноставно приказујемо медведиће на разним местима на екрану. На пример, можемо да уместо позива функције

CrtajMedu(e.Graphics, ClientSize.Width/2, ClientSize.Height / 2);

која црта медведића са главном тачком у центру прозора (као што је и био), употребимо следећа два позива:

CrtajMedu(e.Graphics, ClientSize.Width/2 - 120, ClientSize.Height / 2);
CrtajMedu(e.Graphics, ClientSize.Width/2 + 120, ClientSize.Height / 2);
https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_movable_bears.png

Испробајте ово! Било би знатно теже нацртати другог медеведића да нисмо почетни програм прилагодили за овакву употребу.

Задаци за вежбу

Ако сте при вежбању израчунавања координата писали програме који цртају саобраћајне знаке, можете сада те програме да искористите и да вежбате понављање једног цртежа на више места.

https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_calc_no_parking.png https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_calc_alternating_parking.png https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_calc_camping_ground.png https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_calc_pass_advantage.png https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_calc_traffic_lane_closure.png https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_calc_highway_end.png

У наставку је задатак са кодом који црта непомичну слику, а програм треба преправити тако да се слика лако помера и црта на више места.

Положај куће

Рецимо да сте написали ову функцију за цртање, а циљ вам је да преправите програм тако да кућица може једноставно да се премешта:

private void Form1_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    Brush braonCetka = new SolidBrush(Color.Brown);
    Brush kakiCetka = new SolidBrush(Color.Khaki);
    Brush crvenaCetka = new SolidBrush(Color.Red);
    Point[] krov = new Point[] { new Point(50, 150), new Point(120, 100), new Point(190, 150) };


    g.FillPolygon(crvenaCetka, krov); // krov
    g.FillRectangle(kakiCetka, 50, 150, 140, 100);  // kuca
    g.FillRectangle(braonCetka, 60, 170, 30, 30);   // levi prozor
    g.FillRectangle(braonCetka, 150, 170, 30, 30);  // desni prozor
    g.FillRectangle(braonCetka, 100, 180, 40, 70);  // vrata
}
https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_movable_house.png

Нека је главна тачка (x, y) = (50, 150). Напишите функцију private void Kuca(Graphics g, int x, int y, Color bojaZidova), у којој се црта (помична) кућа. Када се уверите да цртежи у два програма изгледају исто, испред позива Kuca(g, 50, 150, Color.Khaki); додајте још 3 нова позива (и прилагодите величину формулара) да бисте добили слику попут оне испод.

private void Form1_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    Kuca(g, 150, 90, Color.FromArgb(220, 220, 220));
    Kuca(g, 220, 130, Color.White);
    Kuca(g, 350, 160, Color.FromArgb(255, 255, 150));
    Kuca(g, 50, 150, Color.Khaki);
}
https://petljamediastorage.blob.core.windows.net/root/Media/Default/Kursevi/Srednja/cs/drawing_movable_houses.png