{"id":548,"date":"2017-01-24T19:02:46","date_gmt":"2017-01-24T19:02:46","guid":{"rendered":"http:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/?p=548"},"modified":"2017-01-26T08:38:39","modified_gmt":"2017-01-26T08:38:39","slug":"processing-oscyloskop-starcie-nr-3","status":"publish","type":"post","link":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/?p=548","title":{"rendered":"Processing &#8212; oscyloskop (starcie nr 3)"},"content":{"rendered":"<h2>Oscyloskop<\/h2>\n<p><a href=\"http:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/?p=526\">Kontynuujemy<\/a> tworzenie oscyloskopu w <a href=\"http:\/\/processing.org\">processing<\/a>u. Tym razem chcemy doda\u0107 (proste) skalowanie wykresu oraz obs\u0142ug\u0119 klawiatury. Na koniec s\u0142\u00f3w kilka o optymalizacji kodu &#8211; uczmy si\u0119 tworzy\u0107 <strong>m\u0105dre<\/strong> oprogramowanie. Uruchamiamy Arduino ze <a href=\"http:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/?p=526\">skeczem z poprzednich zaj\u0119\u0107<\/a> i koncentrujemy si\u0119 na processingu.<\/p>\n<h2>Skalowanie<\/h2>\n<p>Stosujemy funkcj\u0119 liniow\u0105 y=ax+b aby dopasowa\u0107 otrzymywane liczby z Arduino (nazwijmy je&nbsp;<strong>x<\/strong>-ami) do wysoko\u015bci okienka graficznego Processinga (nazwijmy je&nbsp;<strong>y<\/strong>-kami). Arduino &#8222;nadaje&#8221; nam <strong>x<\/strong> z przedzia\u0142u 0..1023, a okienko z naszym wykresem ma&nbsp;<a href=\"https:\/\/processing.org\/reference\/height.html\">height<\/a> pikseli wysoko\u015bci, czyli <strong>y<\/strong> jest z przedzia\u0142u 0..height.&nbsp;Rysujemy prostok\u0105ty dla ka\u017cdej otrzymanej liczby &#8211; cho\u0107 dla jasno\u015bci kodu powr\u00f3\u0107my na chwil\u0119 do rysowania tych prostok\u0105t\u00f3w od g\u00f3ry ekranu.&nbsp;<\/p>\n<h3>Funkcja <strong>rect<\/strong>(x0,y0,szer,wys) &#8211; przypomnienie<\/h3>\n<p>Przypominam, \u017ce u\u017cywamy funkcji <a href=\"https:\/\/processing.org\/reference\/rect_.html\">rect<\/a>(x0,y0, szer, wys) do rysowania prostok\u0105ta. Para liczb <em>x0,y0<\/em> to wsp\u00f3\u0142rz\u0119dne lewego g\u00f3rnego rogu, natomiast <em>szer,wys<\/em> to odpowiednio szeroko\u015b\u0107 i wysoko\u015b\u0107 prostok\u0105ta. Ostatnia para liczb liczona jest wzgl\u0119dem <em>x0,y0<\/em> wi\u0119c wsp\u00f3\u0142rz\u0119dne przeciwleg\u0142ego rogu prostok\u0105ta (prawy dolny r\u00f3g) wynosi (<em>x0+szer, y0+wys<\/em>). Warto zaznaczy\u0107, \u017ce liczby <em>szer,wys<\/em> mog\u0105 by\u0107 ujemne, co wykorzystamy za chwil\u0119.<\/p>\n<h3>Wracamy do skalowania<\/h3>\n<p>Po przypomnieniu o funkcji rect wracamy do rysowania przeskalowanych prostok\u0105t\u00f3w &#8211; \u017c\u0105damy wi\u0119c, aby spe\u0142nione by\u0142o:<\/p>\n<ol>\n<li>dla wczytanego&nbsp;<strong>x=zero<\/strong> mamy mie\u0107 minimaln\u0105 wysoko\u015b\u0107 ekranu, czyli <strong>y=zero<\/strong>,&nbsp;oraz<\/li>\n<li>dla wczytanego&nbsp;<strong>x=1023<\/strong> (maksymalna liczba nadawana z Arduino) mamy mie\u0107 maksymaln\u0105 wsp\u00f3\u0142rz\u0119dn\u0105&nbsp;<strong>y<\/strong>, czyli&nbsp;<strong>h=height<\/strong>.<\/li>\n<\/ol>\n<p>Powy\u017csze warunki dla skalowania liniowego y=<strong>a<\/strong>x+<strong>b<\/strong> przyjmuj\u0105 matematyczn\u0105 posta\u0107 (nie znamy jeszcze wsp\u00f3\u0142czynnik\u00f3w skalowania&nbsp;<strong>a<\/strong> i&nbsp;<strong>b<\/strong>):<\/p>\n<ol>\n<li>0=<strong>a<\/strong>*0+<strong>b<\/strong><\/li>\n<li>height=<strong>a<\/strong>*1023+<strong>b<\/strong><\/li>\n<\/ol>\n<p>Musimy rozwi\u0105za\u0107 uk\u0142ad r\u00f3wna\u0144 1. i 2. wyznaczaj\u0105c <strong>a<\/strong> i&nbsp;<strong>b<\/strong>.&nbsp;Nie jest to trudne, gdy\u017c momentalnie otrzymujemy:<\/p>\n<p><strong>b=0<\/strong> oraz&nbsp;<strong>a=heigh\/1023<\/strong>.<\/p>\n<p>Dlatego ulepszona funkcja rysuj\u0105ca prostok\u0105ty (od g\u00f3ry ekranu) wygl\u0105da nast\u0119puj\u0105co:<\/p>\n<pre class=\"lang:arduino decode:true\" title=\"Skalowanie + rysowanie prostok\u0105t\u00f3w - z g\u00f3ry okienka.\">float y;\r\nvoid draw(){\r\n int x=getValue(); \r\n print(\"z Arduino x=\",x);\r\n y=height\/1023.0*x;\r\n rect(xPos, 0, 10, y);\r\n println(\" y=\", y);\r\n \r\n xPos=xPos+10;\r\n if (xPos&gt;=width){\r\n   xPos=0;\r\n   background(0);\r\n }\r\n}\r\n<\/pre>\n<p>Na uwag\u0119 zas\u0142uguj\u0105:<\/p>\n<ul>\n<li>utworzenie pomocniczej zmiennej&nbsp;<strong>y<\/strong> przechowuj\u0105cej warto\u015b\u0107 przeskalowanego&nbsp;<strong>x<\/strong>-a<\/li>\n<li>wyliczanie&nbsp;<strong>y<\/strong>-ka wykorzystuje dzielenie liczb rzeczywistych &#8211; zwr\u00f3\u0107 uwag\u0119 na liczb\u0119 1023.0 pisan\u0105 z kropeczk\u0105. Bez takiego post\u0119powania otrzymywaliby\u015bmy zero, jeden lub dwa (liczby ca\u0142kowite), gdy\u017c dzielenie warto\u015bci height (liczba typu int) przez 1023 (bez kropeczki &#8211; te\u017c liczba typu int!) pomija cz\u0119\u015b\u0107 u\u0142amkow\u0105.&nbsp;<\/li>\n<li>uzbrajam kod w wypisywanie na ekran r\u00f3\u017cnych pomocniczych informacji, tutal: wczytanego &#8222;go\u0142ego&#8221;&nbsp;<strong>x<\/strong>-a, oraz przeskalowanego&nbsp;<strong>y<\/strong>-ka (aby wszystko dobrze kontrolowa\u0107).<\/li>\n<\/ul>\n<p>Mamy ju\u017c dzia\u0142aj\u0105ce skalowanie, dzi\u0119ki czemu liczby nie &#8222;wsykocz\u0105&#8221; poza ekran okienka!<\/p>\n<p><strong><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-552 aligncenter\" src=\"http:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy30-300x190.png\" alt=\"\" width=\"300\" height=\"190\" srcset=\"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy30-300x190.png 300w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy30-768x487.png 768w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy30-800x509.png 800w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy30.png 802w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/>Sukces!<\/strong>&nbsp;dlatego lekko to ulepszymy rysuj\u0105c prostok\u0105ty od do\u0142u ekranu:<\/p>\n<pre class=\"lang:arduino decode:true\" title=\"Skalowanie + rysowanie prostok\u0105t\u00f3w - z do\u0142u okienka.\">float y;\r\nvoid draw(){\r\n int x=getValue(); \r\n print(\"z Arduino x=\",x);\r\n y=height\/1023.0*x;\r\n rect(xPos, height, 10, -y);\r\n println(\" y=\", y);\r\n \r\n xPos=xPos+10;\r\n if (xPos&gt;=width){\r\n   xPos=0;\r\n   background(0);\r\n }\r\n}\r\n<\/pre>\n<p>Ca\u0142a zmiana polega na &#8222;zaczepieniu&#8221; lewego-g\u00f3rnego rogu prostok\u0105ta na dole okienka graficznego &#8211; wsp\u00f3\u0142rz\u0119dna (xPos, height), nadanie mu szeroko\u015bci =10 pikseli a wysoko\u015bci&#8230; w\u0142a\u015bnie&nbsp;<strong>-y (minus y)<\/strong>. Nie powinno to zaskakiwa\u0107 w \u015bwietle przypomnienia o funkcji rect (przeczytaj o tym wy\u017cej), bo w\u00f3wczas wsp\u00f3\u0142rz\u0119dna prawego-dolnego rogu prostok\u0105ta wynosi w\u0142a\u015bnie (xPos+10, height-y). Otrzymujemy:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-553 aligncenter\" src=\"http:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy31-300x190.png\" alt=\"\" width=\"300\" height=\"190\" srcset=\"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy31-300x190.png 300w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy31-768x487.png 768w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy31-800x509.png 800w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy31.png 802w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/p>\n<p>Mo\u017cemy by\u0107 ju\u017c z siebie dumni \ud83d\ude09<\/p>\n<h2>Obs\u0142uga klawiatury&nbsp;<\/h2>\n<p>Dodajemy now\u0105 funkcjonalno\u015b\u0107 do naszego programu &#8212; chcemy, aby <strong>klawisz spacja<\/strong> zatrzymywa\u0142 rysowanie wykresu. Arduino ciagle dzia\u0142a i nie przestaje nadawa\u0107 liczb na port szeregowy, tym samym wszystkie liczby nie s\u0105 odbierane przez processing i je &#8222;tracimy&#8221; &#8211; mo\u017cna pomy\u015ble\u0107 o stworzeniu bufora na te liczby, ale dla prostoty my tak nie b\u0119dziemy robi\u0107.<\/p>\n<h3>Funkcja <strong>keyReleased<\/strong>()&nbsp;<\/h3>\n<p><a href=\"https:\/\/processing.org\/reference\/keyReleased_.html\">Obs\u0142uguje klawiatur\u0119<\/a> w processingu &#8211; wystarczy, \u017ce w jej cia\u0142o wpiszemy obs\u0142ug\u0119 konkretnych klawiszy, a wysztko b\u0119dzie dzia\u0142a\u0107 automatycznie &#8211; za to w\u0142a\u015bnie kochamy processing! Zacznijmy od prostego przyk\u0142adu:<\/p>\n<pre class=\"lang:default decode:true\" title=\"Testowanie obs\u0142ugi klawiatury - klawisz SPACJA.\">void keyReleased() {\r\n  if (key == ' ') \r\n    println(\"SPACJA!\")\r\n}\r\n<\/pre>\n<p>Dodanie powy\u017cszej funkcji do naszego programu skutkuje tym, \u017ce przy ka\u017cdym naci\u015bni\u0119ciu spacji w konsoli tekstowej processinga pojawi si\u0119 napis &#8222;SPACJA!&#8221;. Fajnie! i jeszcze raz podkre\u015bl\u0119 &#8211; jak prosto.<\/p>\n<p>A co to jest tutaj zmienna <a href=\"https:\/\/processing.org\/reference\/key.html\">key<\/a>? Ot\u00f3\u017c jest to jedna z wielu zmiennych, kt\u00f3rych nie trzeba deklarowa\u0107 w processingu, a kt\u00f3ra przechowuje kod ostatnio wci\u015bni\u0119tego klawisza na klawiaturze.<\/p>\n<p>Jak wi\u0119c uruchamia\u0107 i zatrzymywa\u0107 tworzenie wykresu wciskaj\u0105c spacj\u0119? W tym celu dodajemy pomocnicz\u0105 zmienn\u0105&nbsp;<strong>stop<\/strong> i robimy co\u015b takiego:<\/p>\n<pre class=\"lang:default decode:true\" title=\"Testowanie obs\u0142ugi klawiatury - klawisz SPACJA ON\/OFF.\">boolean stop=false;\r\nvoid keyReleased() {\r\n  if (key == ' ') {\r\n    stop=!stop;\r\n    if (stop)\r\n      println(\"PAUZA\");\r\n    else\r\n      println(\"GO!\");\r\n  }\/\/if spacja\r\n}\r\n<\/pre>\n<p>Obja\u015bnienia:<\/p>\n<ul>\n<li>zmianna typu&nbsp;<strong>boolean<\/strong> przyjmuje dwie warto\u015bci &#8211; <em>true<\/em> lub <em>false<\/em>&nbsp;(inaczej m\u00f3wi\u0105c <em>tak<\/em> i <em>nie<\/em>, <em>zero<\/em> i <em>jeden<\/em>&#8230;) i informuje nas, czy zatrzymujemy wykres czy nie,<\/li>\n<li>w funkcji keyReleased() sprawdzamy, czy wci\u015bni\u0119to spacj\u0119 czy nie, a je\u015bli tak to zmieniamy warto\u015b\u0107 zmiennej&nbsp;<strong>stop<\/strong> na przeciwn\u0105 (instrukcja <strong>stop=!stop)<\/strong>. Nast\u0119pnie wypisujemy odpowiedni komunikat.<\/li>\n<\/ul>\n<p>Aby nie tylko wypisywany by\u0142 odpowiedni komunikat ALE i faktycznie wykres przestawa\u0142 si\u0119 rysowa\u0107, nale\u017cy zmodyfikowa\u0107 funkcj\u0119 <strong>draw<\/strong>,&nbsp;odpowiedzialn\u0105 za rysowanie wykresu,&nbsp;w odpowiedni spos\u00f3b (uwzgl\u0119dniaj\u0105cy istnienie zmiennej&nbsp;<strong>stop<\/strong>):<\/p>\n<pre class=\"lang:arduino mark:4,16 decode:true\" title=\"Wykres - zatrzymywanie\/wznawianie klawiszem SPACJA.\">boolean stop=false;\r\nfloat y;\r\nvoid draw(){\r\n  if (!stop){\r\n    int x=getValue(); \r\n    print(\"z Arduino x=\",x);\r\n    y=height\/1023.0*x;\r\n    rect(xPos, height, 10, -y);\r\n    println(\" y=\", y);\r\n \r\n    xPos=xPos+10;\r\n    if (xPos&gt;=width){\r\n      xPos=0;\r\n      background(0);\r\n   }\r\n  }\/\/if stop\r\n}\r\n<\/pre>\n<p>Jak wida\u0107 zmiana polega na dodaniu wa\u017cnego&nbsp;<strong>if<\/strong>-a, kt\u00f3ry sprawdza, czy mam rysowa\u0107 wykres czy nie. Je\u015bli&nbsp;<strong>stop=false<\/strong>&nbsp;to negacja tego warunku jest prawd\u0105, wi\u0119c &nbsp;wykonuj\u0105 si\u0119 instrukcje z linii 5-14. Je\u015bli za\u015b <strong>stop=true<\/strong> (czyli po pierwszym wci\u015bni\u0119ciu spacji) warunek z linii 4 nie jest spe\u0142niony (negacja prawdy to nieprawda!) i omijamy&nbsp;ca\u0142\u0105 procedur\u0119 pobrania&nbsp;<strong>x-<\/strong>a z portu szeregowego, przeskalowanie, wypisywanie pomocniczych rzeczy w konsoli i kre\u015blenie prostok\u0105t\u00f3w (linie 5-14) i l\u0105dujemy w linii 16 (koniec if-a). Ale to ju\u017c ostatnia linia funkcji draw wi\u0119c&#8230; zgodnie z zasadami processinga funkcja draw wykonuje si\u0119 ponownie. Przypomn\u0119, \u017ce Arduino nie zaprzestaje nadawania liczb (nie sterujemy Arduino, tylko processingiem) wi\u0119c dane te s\u0105 tracone.<\/p>\n<h2>Rysowanie linii (zamiast prostok\u0105t\u00f3w)<\/h2>\n<p>Zmieniamy rysowanie prostok\u0105t\u00f3w na rysowanie linii. Ma to swoje uzasadnienie &#8211; w przysz\u0142o\u015bci kre\u015bli\u0107 b\u0119dziemy nie dane z jednego portu analogowego Arduino, ale z dw\u00f3ch, trzech&#8230; Prostok\u0105ty w takim przypadku pokrywa\u0142yby si\u0119 i wykresy nie by\u0142by czytelny. Dlatego kre\u015blimy lini\u0119.<\/p>\n<p><a href=\"https:\/\/processing.org\/reference\/line_.html\">line<\/a>(x1,y1, x2, y2) &#8212; pierwsza para liczb to wsp\u00f3\u0142rz\u0119dne pocz\u0105tku linii, druga para liczb &#8211; wsp\u00f3\u0142rz\u0119dne ko\u0144ca linii. U nas linia oddalona jest o 10 pikseli w x-ach (czyli x2=x1+10) ale wsp\u00f3\u0142rz\u0119dne y linii zale\u017c\u0105 od danych z Arduino: y1 to poprzednia dana, y2 to dana aktualnie otrzymana (oczywi\u015bcie ju\u017c po przeskalowaniu funkcj\u0105 liniow\u0105). Skoro musimy pami\u0119ta\u0107 poprzedni\u0105 dan\u0105 to wprowadzamy pomocnicz\u0105 zmienn\u0105 do jej przechowywania&nbsp;<strong>poprzedniY<\/strong> i modyfikujemy kod w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<pre class=\"lang:arduino mark:2,8,9 decode:true\" title=\"Rysowanie linii (zamiast prostok\u0105t\u00f3w)\">boolean stop=false;\r\nfloat y, poprzedniY;\r\nvoid draw(){\r\n  if (!stop){\r\n    int x=getValue(); \r\n    print(\"z Arduino x=\",x);\r\n    y=height\/1023.0*x;\r\n    line(xPos-10, poprzedniY, xPos, y);\r\n    poprzedniY = y;\r\n    println(\" y=\", y);\r\n \r\n    xPos=xPos+10;\r\n    if (xPos&gt;=width){\r\n      xPos=0;\r\n      background(0);\r\n   }\r\n  }\/\/if stop\r\n}\r\n<\/pre>\n<p>Zwracam uwag\u0119 na:<\/p>\n<ul>\n<li>dodanie pomocniczej zmiennej <em>poprzedniY<\/em> (linia 2)<\/li>\n<li>rysowanie linii uwzgl\u0119dniaj\u0105ce poprzedni pomiar (linia 8)<\/li>\n<li>zamiana aktualnego y-ka na poprzedniY, ale ju\u017c PO narysowaniu linii (linia 9)<\/li>\n<li>ustali\u0142em grubo\u015b\u0107 linii na 10 pikseli funkcj\u0105 <a href=\"https:\/\/processing.org\/reference\/strokeWeight_.html\">strokeWeight()<\/a>&nbsp;&#8211; zrobi\u0142em to na pocz\u0105tku programu w funkcji setup()<\/li>\n<li>ustali\u0142em kolor linii na czerwony funkcj\u0105 <a href=\"https:\/\/processing.org\/reference\/stroke_.html\">stroke<\/a>() &#8211; tak\u017ce w funkcji setup()<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-556 aligncenter\" src=\"http:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy32-300x190.png\" alt=\"\" width=\"300\" height=\"190\" srcset=\"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy32-300x190.png 300w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy32-768x487.png 768w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy32-800x509.png 800w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy32.png 802w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><br \/>\nPrzy pierwszym uruchomieniu programu warto\u015b\u0107 zmiennej&nbsp;<em>poprzedniY<\/em> jest nieustalona co nie jest dobrze, ale nam to nie przeszkadza &#8211; to tylko 1 pomiar (po nim wszystko ju\u017c dobrze dzia\u0142a). Dlatego tym niuansikiem nie b\u0119dziemy si\u0119 dalej zajmowa\u0107.<\/p>\n<h2>Ulepszenie skalowania (klawisze z\/Z jak zoom)<\/h2>\n<p>Jak na razie wykorzystujemy potencjometr jako \u017ar\u00f3d\u0142o danych do wy\u015bwietlania, wi\u0119c u nas Arduino wysy\u0142a liczby z pe\u0142nego zakresu 0..1023 (odpowiadaj\u0105ce napi\u0119ciom z przedzia\u0142u 0..5V). Ale przecie\u017c zale\u017cy nam na pod\u0142\u0105czaniu r\u00f3\u017cnych czujek do Arduino i tworzenia wykres\u00f3w, wi\u0119c nie zawsze tak b\u0119dzie. Mo\u017ce si\u0119 bowiem okaza\u0107, \u017ce czujka &#8222;produkuje&#8221; napi\u0119cia z zakresu 0..1V i w\u00f3wczas&#8230; bardzo du\u017ca cz\u0119\u015b\u0107 naszego wykresu nie jest wykorzystywana! Co wi\u0119cej, je\u015bli w takim przypadku napi\u0119cia oscyluj\u0105, to my tracimy te informacje &#8211; wykres jest za ma\u0142o czytelny, bo &#8222;schowany&#8221; na dole ekranu a wi\u0119c i te oscylacje s\u0105 malutkie, nieczytelne. Przyk\u0142ad:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-563 aligncenter\" src=\"http:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy34-300x190.png\" alt=\"\" width=\"300\" height=\"190\" srcset=\"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy34-300x190.png 300w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy34-768x487.png 768w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy34-800x509.png 800w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy34.png 802w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/p>\n<p>Jak temu zaradzi\u0107? Bardzo prosto &#8211; wprowadzamy dodatkow\u0105 zmienn\u0105 okre\u015blaj\u0105c\u0105 maksymaln\u0105 liczb\u0119 (zmienna <strong>ML<\/strong>) zamiast dotychczasowej liczby 1023 dla jakiej odbywa si\u0119 skalowanie wysoko\u015bci ekranu. Dzi\u0119ki zmianie&nbsp;<strong>ML<\/strong>-a b\u0119dziemy mogli &#8222;zajrze\u0107 w g\u0142\u0105b&#8221; wykresu, czyli ograniczy\u0107 zakres wykresu &#8211; powstanie &#8222;zoom&#8221; \ud83d\ude09<\/p>\n<p>Kontrol\u0119 nad zmienn\u0105&nbsp;<strong>ML<\/strong> odbywa si\u0119 z klawiatury za pomoc\u0105 klawiszy 'z&#8217; (ma\u0142e zet) oraz 'Z&#8217; (du\u017ce zet), w tan prosty spos\u00f3b, \u017ce odpowiednio zmniejszamy\/zwi\u0119kszamy warto\u015b\u0107&nbsp;<strong>ML<\/strong> dwa razy. Kod funkcji keyReleased pod zmianach:<\/p>\n<pre class=\"lang:default decode:true\" title=\"Obs\u0142uga ZOOMu z klawiatury.\">float ML=1024;\r\nboolean stop=false;\r\nvoid keyReleased() {\r\n  if (key == ' ') {\r\n    stop=!stop;\r\n    if (stop)\r\n      println(\"PAUZA\");\r\n    else\r\n      println(\"GO!\");\r\n  }\/\/if spacja\r\n  if (key == 'z') ML=ML\/2;\r\n  if (key == 'Z') ML=ML*2;\r\n}\r\n<\/pre>\n<p>Dodatkowo nale\u017cy zmieni\u0107 skalowanie warto\u015bci&nbsp;<strong>y<\/strong> w funkcji draw() na nast\u0119puj\u0105ce:&nbsp;<\/p>\n<pre class=\"start-line:7 lang:default decode:true\" title=\"Zmiana do funkcji draw().\">y=(float)height\/ML*w;\r\n<\/pre>\n<p>co wynika z nowych \u017c\u0105da\u0144 dotycz\u0105cych skalowania:<\/p>\n<ol>\n<li>dla wczytanego&nbsp;<strong>x=zero<\/strong> mamy mie\u0107 minimaln\u0105 wysoko\u015b\u0107 ekranu, czyli <strong>y=zero<\/strong>,&nbsp;oraz<\/li>\n<li>dla wczytanego&nbsp;<strong>x=ML<\/strong>&nbsp;(maksymalna liczba nadawana z Arduino) mamy mie\u0107 maksymaln\u0105 wsp\u00f3\u0142rz\u0119dn\u0105&nbsp;<strong>y<\/strong>, czyli&nbsp;<strong>h=height<\/strong>.<\/li>\n<\/ol>\n<p>Warto te\u017c zmodyfikowa\u0107 wy\u015bwietlanie pomocniczych informacji w okienku terminala na uwzgl\u0119dniaj\u0105ce wypisywanie aktualnej warto\u015bci <strong>ML<\/strong>-a, czyli:<\/p>\n<pre class=\"start-line:10 lang:arduino decode:true \" title=\"Zmiany w kodzie funkcji draw().\">    println(\" y=\", y, \" ML=\", ML);\r\n<\/pre>\n<p>Mamy teraz prost\u0105 mo\u017cliwo\u015b\u0107 zaw\u0119\u017cania widoku wykresu do warto\u015bci&nbsp;<strong>ML<\/strong>, kt\u00f3r\u0105 kontrolujemy klawiszami z\/Z. Oczywi\u015bcie warto zadba\u0107 o to, aby zmniejszaj\u0105c warto\u015b\u0107&nbsp;<strong>ML<\/strong> nie spa\u015b\u0107 na zero (co si\u0119 mo\u017ce zdarzy\u0107), wi\u0119c nale\u017cy doda\u0107 kolejnego if-a.<\/p>\n<h2>Wi\u0119ksza kontrola nad skalowaniem wykresu<\/h2>\n<p>Warto te\u017c wprowadzi\u0107 inne klawisze kontroluj\u0105ce wawrto\u015b\u0107 <strong>ML<\/strong>-a, gdy\u017c dzielenie\/mno\u017cenie przez 2 jest dobre tylko na pocz\u0105tek zabawy. Inne klawisze mog\u0105 zmienia\u0107 <strong>ML <\/strong>np.<strong>&nbsp;<\/strong>o 100 &#8211; dbaj\u0105c tak\u017ce o to, aby nie uzyska\u0107 w ten spos\u00f3b liczb ujemnych).<\/p>\n<p>Dodatkowo mo\u017cna te\u017c rozwa\u017cy\u0107 sytuacj\u0119, \u017ce otrzymywane napi\u0119cie nie b\u0119dzie z przedzia\u0142u 0..1V a np. 0.5..0.7V &#8212; w\u00f3wczas wykres mo\u017ce by\u0107 ponownie nieczytelny, bo my kontrolujemy tylko maksymaln\u0105 liczb\u0119, a nie minimaln\u0105 (u nas ona zawsze wynosi zero!). Mo\u017cna wi\u0119c wprowadzi\u0107 kolejn\u0105 liczb\u0119 pomocnicz\u0105&nbsp;<strong>mL<\/strong> &#8211; minimalna liczba i j\u0105 zmienia\u0107 z klawiatury&#8230; W\u00f3wczas uk\u0142ad r\u00f3wna\u0144 przy skalowaniu musi by\u0107 zbudowany z \u017c\u0105da\u0144:<\/p>\n<ol>\n<li>dla wczytanego&nbsp;<strong>x=mL<\/strong>&nbsp;mamy mie\u0107 minimaln\u0105 wysoko\u015b\u0107 ekranu, czyli <strong>y=zero<\/strong>,&nbsp;oraz<\/li>\n<li>dla wczytanego&nbsp;<strong>x=ML<\/strong>&nbsp;(maksymalna liczba nadawana z Arduino) mamy mie\u0107 maksymaln\u0105 wsp\u00f3\u0142rz\u0119dn\u0105&nbsp;<strong>y<\/strong>, czyli&nbsp;<strong>h=height<\/strong>.<\/li>\n<\/ol>\n<p>T\u0119 modyfikacj\u0119 pozostawiam jako zadanie domowe \ud83d\ude09<\/p>\n<h2>Ulepszenie rysowania (klawisze x\/X do historii wykresu)<\/h2>\n<p>Aktulanie nasze okienko ma szeroko\u015b\u0107 800 pikseli, a linie\/prostok\u0105ty s\u0105 rysowane co 10 pikseli. Oznacza to, \u017ce na jednym pe\u0142nym ekranie zmie\u015bcimy maksymalnie 80 punkt\u00f3w nades\u0142anych z Arduino. W ten spos\u00f3b powstaje historia pomiar\u00f3w o d\u0142ugo\u015bci 80 pomiar\u00f3w. Je\u015bli jednak zmienimy szeroko\u015b\u0107 linii\/prostok\u0105t\u00f3w z 10 pikseli na 5, to ta historia wynosi ju\u017c 160 pomiar\u00f3w. Je\u015bli zmienimy szeroko\u015b\u0107 na 2, to mamy ju\u017c histori\u0119 400 punkt\u00f3w. Wiadomo, do czego zmierzam? Nale\u017cy wprowadzi\u0107 zmienn\u0105 kontroluj\u0105c\u0105 t\u0105 szeroko\u015b\u0107, nazwijmy j\u0105&nbsp;<strong>skokX<\/strong> (o tyle punkt\u00f3w &#8222;skaczemy&#8221; w x-ach rysuj\u0105c lini\u0119\/prostok\u0105t) i wprowad\u017amy mo\u017cliwo\u015b\u0107 jej zmiany za pomoc\u0105 przycisk\u00f3w x\/X na klawiaturze.<\/p>\n<pre class=\"lang:arduino decode:true\" title=\"Zag\u0119szczanie historii wykresu - skokX.\">int skokX=10;\r\nboolean stop=false;\r\nfloat y, poprzedniY;\r\nvoid draw(){\r\n  if (!stop){\r\n    int x=getValue(); \r\n    print(\"z Arduino x=\",x);\r\n    y=height\/1023.0*x;\r\n    line(xPos-skokX, poprzedniY, xPos, y);\r\n    poprzedniY = y;\r\n    println(\" y=\", y);\r\n \r\n    xPos=xPos+skokX;\r\n    if (xPos&gt;=width){\r\n      xPos=0;\r\n      background(0);\r\n   }\r\n  }\/\/if stop\r\n}\r\n<\/pre>\n<p>Zmiany s\u0105 niewielkie:<\/p>\n<ul>\n<li>wprowadzili\u015bmy zmienn\u0105&nbsp;<strong>skokX<\/strong> z warto\u015bci\u0105 pocz\u0105tkow\u0105 10 (linia 1), oraz<\/li>\n<li>zast\u0105pili\u015bmy liczb\u0119 10 zmienn\u0105&nbsp;<strong>skokX<\/strong> w dw\u00f3ch miejscach &#8211; w rysowaniu (linia 9) oraz w miejscu zwi\u0119kszania pozycji xPos (linia 13).&nbsp;<\/li>\n<\/ul>\n<p>Nale\u017cy te\u017c zmodyfikowa\u0107 funkcj\u0119 obs\u0142ugi klawiatury o reakcj\u0119 na wciskanie klawiszy x\/X &#8211; powiedzmy odejmujemy\/dodajemy warto\u015b\u0107 1 do aktualnej warto\u015bci&nbsp;<strong>skokX<\/strong>. Nale\u017cy jednak uwzgl\u0119dni\u0107 sytuacj\u0119, \u017ceby nie zmniejszy\u0107 tej zmiennej do zera (lub poni\u017cej).<\/p>\n<pre class=\"lang:default decode:true\" title=\"Obs\u0142uga historii wykresu z klawiatury.\">void keyReleased(){\r\n  if (key == ' '){\r\n    stop=!stop;\r\n    if (stop)\r\n      print(\"PAUZA\");\r\n    else\r\n      print(\"WZN\u0102\u201cW\");\r\n    };\r\n  if (key == 'z') ML=ML\/2;\r\n  if (key == 'Z') ML=ML*2;\r\n  if (key == 'x') skokX-=1;  \r\n  if (key == 'X') skokX+=1;\r\n  \/\/dodatkowe sprawdzenie\r\n  if (skokX &lt; 1) skokX=1;\r\n}<\/pre>\n<p>Wynik dzia\u0142ania programu mo\u017ce wygl\u0105da\u0107 tak (widzimy d\u0142uuuug\u0105 histori\u0119 pomiar\u00f3w):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-558 aligncenter\" src=\"http:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy33-300x190.png\" alt=\"\" width=\"300\" height=\"190\" srcset=\"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy33-300x190.png 300w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy33-768x487.png 768w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy33-800x509.png 800w, https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/wp-content\/uploads\/sites\/6\/2017\/01\/oscy33.png 802w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/p>\n<h2>Optymalizacja<\/h2>\n<p>Czyli jak &#8222;kodowa\u0107&#8221;, aby nasz program dzia\u0142a\u0142 szybciej. Nawet je\u015bli teraz dzia\u0142a szybko, to i tak warto uczy\u0107 si\u0119 optymalizacji. Je\u015bli tego nie b\u0119dziemy robi\u0107, to b\u0119dziemy marnowa\u0107 zasoby komputera (pami\u0119\u0107, CPU) na niepotrzebne instrukcje&#8230; Zaczynamy!<\/p>\n<p>Przypatrzmy si\u0119 funkcji&nbsp;<strong>keyReleassed()<\/strong> &#8211; mamy tam szereg if-\u00f3w. Jest raczej oczywiste, \u017ce musz\u0105 tam one by\u0107, ale nie w tej postaci. Bo je\u015bli wci\u015bni\u0119to klawisz Z to wykonujemy odpowiedni\u0105 akcj\u0119, ale po co sprawdza\u0107 dalej, czy wci\u015bni\u0119to klawisz x albo X? przecie\u017c ju\u017c obs\u0142u\u017cyli\u015bmy wci\u015bni\u0119cie przycisku Z. Dlatego wypada to zrobi\u0107 z szeregiem if-els\u00f3w, jako\u015b tak:<\/p>\n<pre class=\"lang:default decode:true\" title=\"Optymalizacja if-elsami.\">void keyReleased(){\r\n  if (key == 'z') ML=ML\/2;\r\n  else\r\n    if (key == 'Z') ML=ML*2;\r\n    else\r\n      if (key == 'x') skokX-=1;  \r\n      else\r\n        if (key == 'X') skokX+=1;\r\n  \/\/dodatkowe sprawdzenie\r\n  if (skokX &lt; 1) skokX=1;\r\n}<\/pre>\n<p>Teraz jest zdecydowanie lepiej! Wyobra\u017amy sobie, \u017ce jedno por\u00f3wnanie kosztuje 1$ (w komputerze <em>walut\u0105<\/em> jest u\u0142amek sekundy, jakie CPU musi sp\u0119dzi\u0107 na wykonaniu danej operacji &#8211; ale ja b\u0119d\u0119 pos\u0142ugiwa\u0107 si\u0119 dolarami, bo mocno przemawia to do naszej wyobra\u017ani). Wida\u0107 wi\u0119c, \u017ce je\u015bli wybrano klawisz 'z&#8217; to zap\u0142acimy za wykonanie funkcji tylko 1$, a nie 5$ jak w pierwotnym przyk\u0142adzie &#8211; ogromna oszcz\u0119dno\u015b\u0107!<\/p>\n<p>Wygodnie pos\u0142ugiwa\u0107 si\u0119 instrukcj\u0105 switch-case, zamiast du\u017cej liczby par if-els\u00f3w, w ten oto spos\u00f3b:<\/p>\n<pre class=\"lang:default decode:true\" title=\"Optymalizacja - switch\/case.\">void keyReleased(){\r\n switch (key){\r\n    case ' ': \r\n    stop=!stop;\r\n    if (stop)\r\n      print(\"PAUZA\");\r\n    else\r\n      print(\"GO!\");\r\n    break;\r\n    \r\n    case 'z': ML=ML\/2;break;\r\n    case 'Z': ML=ML*2;break;\r\n    case 'x': skokX-=1;break;\r\n    case 'X': skokX+=1;break;\r\n  }\/\/switch\r\n  \/\/dodatkowe sprawdzenie\r\n  if (skokX &lt; 1) skokX=1;\r\n}<\/pre>\n<p>Od razu zauwa\u017camy te\u017c, \u017ce nie ma co wykonywa\u0107 sprawdzenia if (skokX&lt;1) w ka\u017cdym wywo\u0142aniu funkcji keyReleased(), gdy\u017c to sprawdzenie dotyczy <strong>tylko<\/strong> przypadku zmiany zmiennej skokX &#8211; za pomoc\u0105 klawisza 'x&#8217;. Dlatego to dodatkowe sprawdzenie przenosimy w odpowiednie miejsce:<\/p>\n<pre class=\"lang:default decode:true \">   case 'x': skokX-=1; if (skokX &lt; 1) skok=1; break;\r\n<\/pre>\n<p>i kasujemy z ko\u0144ca funkcji keyReleased(). Prosta sprawa, a poprawiamy wydajno\u015b\u0107 naszego programu.<\/p>\n<p>Kolejn\u0105 optymalizacj\u0105 jest samo skalowanie liczb: w funkcji draw() za ka\u017cdym razem z odebranego x-a robimy odpowiedni (przeskalowany) y-k, co wymaga operacji dzielenia i mno\u017cenia. Du\u017co &#8211; za du\u017co. To dzielenie wynika z wyliczenia wsp\u00f3\u0142czynnika&nbsp;<strong>a<\/strong> dla prostej, kt\u00f3ry przecie\u017c zmienia si\u0119 tylko w\u00f3wczas, gdy zmieniamy zoom (klawisze z\/Z). Je\u015bli nic nie zmieniamy, to po co wykonujemy dzielenie? Dzielenie jest drogie, wi\u0119c warto tego unika\u0107. Dlatego wprowadz\u0119 zmienn\u0105 przechowuj\u0105c\u0105 warto\u015b\u0107 tego wsp\u00f3\u0142czynnika (pami\u0119\u0107 jest tania w por\u00f3wnaniu do dzielenia!), nazw\u0119 go <strong>wspA<\/strong> i b\u0119d\u0119 go wylicza\u0107 tylko w\u00f3wczas, gdy trzeba (klawisze z\/Z).<\/p>\n<pre class=\"lang:default decode:true\" title=\"Optymalizacja - wyliczanie wspA.\">float wspA;\r\nvoid keyReleased(){\r\n switch (key){\r\n case ' ': \r\n   stop=!stop;\r\n   if (stop)\r\n     print(\"PAUZA\");\r\n   else\r\n    print(\"GO!\");\r\n   break;\r\n \r\n   case 'z': ML=ML\/2;wspA=(float)height\/ML;break;\r\n   case 'Z': ML=ML*2;wspA=(float)height\/ML;break;\r\n   case 'x': skokX-=1;if (skokX &lt; 1) skokX=1;break;\r\n   case 'X': skokX+=1;break;\r\n }\/\/swicth\r\n}<\/pre>\n<p>oczywi\u015bcie nale\u017cy tak\u017ce zmodyfikowa\u0107 funkcj\u0119 rysuj\u0105c\u0105 &#8211; draw()<\/p>\n<pre class=\"lang:default mark:5 decode:true \" title=\"Funkcja draw() po optymalizacji.\">void draw(){\r\n if (!stop){\r\n   int x=getValue(); \r\n   print(x);\r\n   y=wspA*w;\r\n   line(xPos-skokX, poprzedniY, xPos, y);\r\n   poprzedniY = y;\r\n   println(\" \", y, \" ML=\",ML, \" skokX=\", skokX);\r\n \r\n   xPos=xPos+skokX;\r\n   if (xPos &gt;= width){\r\n     xPos=0;\r\n     background(0);\r\n   }\r\n }\/\/if stop\r\n}<\/pre>\n<p>i widzimy, \u017ce wyznaczenie y-ka polega teraz ju\u017c na wykonaniu <strong>tylko jednej operacji<\/strong> mno\u017cenia &#8211; bez dzielenia. Sprytne, prawda? (uwaga: nale\u017cy jeszcze doda\u0107 wyliczenie wsp\u00f3\u0142czynnika&nbsp;<strong>wspA<\/strong> w funkcji setup(), aby warto\u015b\u0107 ta by\u0142a ustalona na pocz\u0105tku programu).<\/p>\n<p>B\u0119dzie to tym bardziej efektywne, gdy nasze skalowanie b\u0119dzie pe\u0142ne i uwzgl\u0119dni nie tylko maksymaln\u0105 warto\u015b\u0107&nbsp;<strong>ML<\/strong> ale i minimaln\u0105&nbsp;<strong>mL<\/strong>&nbsp;(o czym pisa\u0142em gdzie\u015b wy\u017cej). W takim przypadku pojawi si\u0119 niezerowy wsp\u00f3\u0142czynnik&nbsp;<strong>b<\/strong> prostej, kt\u00f3ry wymaga kolejnych operacji mno\u017cenia\/dzielenia &#8211; dlatego trzeba wprowadzi\u0107 kolejn\u0105 pomocnicz\u0105 zmienn\u0105, powiedzmy&nbsp;<strong>wspB<\/strong> i post\u0119powa\u0107 podobnie.<\/p>\n<h2>Czego si\u0119 dzi\u015b nauczy\u0142e\u015b<\/h2>\n<ul>\n<li>skalowanie<\/li>\n<li>processing: rysowanie prostok\u0105t\u00f3w<\/li>\n<li>skalowanie&nbsp;<\/li>\n<li>obs\u0142uga klawiatury<\/li>\n<li>skalowanie<\/li>\n<li>instrukcje switch-case<\/li>\n<li>optymalizacja<\/li>\n<li>czy wspomnia\u0142em o skalowaniu? \ud83d\ude09<\/li>\n<\/ul>\n<h2>(ci\u0105gle) pozostaje kilka kwestii do rozwi\u0105zania:<\/h2>\n<ul>\n<li><del>poprawne wczytywanie liczb (z Arduino wysy\u0142amy 0..1023, a processing &#8222;widzi&#8221; tylko jakie\u015b ma\u0142e liczby, &lt;30?)<\/del><\/li>\n<li><del>skalowanie wykresu &#8211; czyli chcemy mie\u0107 tak, by liczby z zakresu 0..1023 zawsze mie\u015bci\u0142y si\u0119 na ekranie, a nie &#8222;wyskakiwa\u0142y&#8221; poza aktualn\u0105 wysoko\u015b\u0107 ekranu<\/del><\/li>\n<li>skalowanie minimalnej liczby (wi\u0119cej skalowa\u0144)<\/li>\n<li>przesuwanie wykresu g\u00f3ra\/d\u00f3\u0142 (wi\u0119cej skalowa\u0144)<\/li>\n<li>obs\u0142uga myszki i wy\u015bwietlanie dowolnych warto\u015bci z historii wykresu<\/li>\n<li>pisanie napis\u00f3w w oknie processinga (np. liczb z warto\u015bciami na wykresach)<\/li>\n<li>pisanie skali na wykresie (np. mL i ML z brzegu okienka)<\/li>\n<li>podpis osi x &#8211; mo\u017cna u\u017cy\u0107 czasu do opisu tej osi, b\u0119dzie wida\u0107 kiedy odebrano konkretn\u0105 dan\u0105<\/li>\n<li>\u0142adniejsza grafika<\/li>\n<\/ul>\n<p>Zapraszam na kolejne zaj\u0119cia!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Oscyloskop Kontynuujemy tworzenie oscyloskopu w processingu. Tym razem chcemy doda\u0107 (proste) skalowanie wykresu oraz obs\u0142ug\u0119 klawiatury. Na koniec s\u0142\u00f3w kilka o optymalizacji kodu &#8211; uczmy si\u0119 tworzy\u0107 m\u0105dre oprogramowanie. Uruchamiamy Arduino ze skeczem z poprzednich zaj\u0119\u0107 i koncentrujemy si\u0119 na processingu. Skalowanie Stosujemy funkcj\u0119 liniow\u0105 y=ax+b aby dopasowa\u0107 otrzymywane liczby z Arduino (nazwijmy je&nbsp;x-ami) do [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":558,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[52],"tags":[116,118,109,120,121,42,119,111,117],"class_list":{"0":"post-548","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","6":"hentry","7":"category-fibot","8":"tag-flaga","9":"tag-funkcja-liniowa","10":"tag-grafika","11":"tag-key","12":"tag-keyreleased","13":"tag-processing","14":"tag-rect","15":"tag-serial-write","16":"tag-skalowanie","18":"post-with-thumbnail","19":"post-with-thumbnail-large"},"_links":{"self":[{"href":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/index.php?rest_route=\/wp\/v2\/posts\/548","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/index.php?rest_route=\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=548"}],"version-history":[{"count":31,"href":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/index.php?rest_route=\/wp\/v2\/posts\/548\/revisions"}],"predecessor-version":[{"id":587,"href":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/index.php?rest_route=\/wp\/v2\/posts\/548\/revisions\/587"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/index.php?rest_route=\/wp\/v2\/media\/558"}],"wp:attachment":[{"href":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=548"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=548"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/physics.uwb.edu.pl\/wf\/fi-bot\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=548"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}