1. Kształty, w których można tworzyć dowolne kształty geometryczne jako kombinacje linii prostych, krzywych, prostokątów, elips i łuków.

Wielkość: px
Rozpocząć pokaz od strony:

Download "1. Kształty, w których można tworzyć dowolne kształty geometryczne jako kombinacje linii prostych, krzywych, prostokątów, elips i łuków."

Transkrypt

1 Java2D, rysowanie okna 1. WSTĘP Kiedy piszesz program z graficznym interfejsem użytkownika, programowanie opiera się w dużej mierze na wykorzystaniu gotowych klas dla okien, przycisków, czcionek itp. Od samego początku klasy te były gromadzone w AWT i jego paczkach oraz Interfejsy API można postrzegać jako dwie warstwy klas, które można nazwać zestawem narzędzi interfejsu użytkownika i zestawem narzędzi do rysowania, przy czym pierwsza warstwa składała się z klas komponentów i właściwości komponentów, natomiast druga warstwa składała się z klas podstawowych narzędzi geometrycznych, które potrafi rysować linie i kwadraty oraz manipulować poszczególnymi pikselami. Później (począwszy od Java 2) AWT został podzielony na dwa interfejsy API w postaci Swing i Java2D. Ta książka dotyczy Java2D, a zatem tego, jak w Javie możesz programować dwuwymiarową grafikę i manipulować obrazami. Podobnie jak inne interfejsy API Java zawiera Java2D bardzo dużą liczbę klas, ale ogólnie są to klasy dla: 1. Kształty, w których można tworzyć dowolne kształty geometryczne jako kombinacje linii prostych, krzywych, prostokątów, elips i łuków. 2. Obrysowanie, które określa sposób rysowania linii pod względem szerokości, linii przerywanej lub ciągłego. Ponadto możesz określić sposób rysowania narożników. 3. Wypełnianie, które określa sposób wypełniania kształtów. Możesz określić kolor, wzór lub wypełnienie gradientowe, a w rzeczywistości możesz również użyć obrazu. 4. Transformacje, w których wszystko, co jest rysowane (kształty, obrazy, tekst) można rozciągać, skalować i obracać. 5. Komponowanie alfa to proces łączenia rysunku z tłem w celu tworzenia figur o częściowej lub pełnej przezroczystości. 6. Przycinanie jest opcją ograniczającą część kształtu do narysowania, aby nie rysowała na niechcianym obszarze okna. 7. Wygładzanie to technika pozwalająca uniknąć nierównych linii, w których widać poszczególne piksele. 8. Tekst, w którym możesz manipulować tekstem w taki sam sposób, jak inne kształty geometryczne. 9. Kolor obejmujący klasy i metody reprezentowania kolorów, dzięki czemu są one niezależne od sprzętu. 10. Obrazy, które umożliwiają manipulowanie obrazami w taki sam sposób, jak inne kształty geometryczne. Obejmuje także zajęcia do odczytywania obrazów i zapisywania obrazów w plikach. 11. Przetwarzanie obrazu, które obejmuje klasy służące do manipulowania poszczególnymi pikselami w obrazach. 12. Animacja, czyli klasy udostępniające usługi, dzięki czemu można animować geometryczne kształty, tekst i obrazy.

2 Pozostała część tego tekstu, w kilku przykładach, dotyczy powyższych 12 pozycji, ale najpierw chcę pokazać program. Program nazywa się HelloJava2D i jeśli uruchomisz program, otworzy się okno, jak pokazano poniżej. Okno rysuje dwie proste linie, prostokąt i dużą literę A. Kod okna jest następujący: package hellojava2d; import java.awt.*; import java.awt.geom.line2d; import java.awt.geom.rectangle2d; import javax.swing.*; public class MainWindow extends JFrame public MainWindow() super("hello Java 2D"); setsize(500, 500); setlocationrelativeto(null); add(new Drawing()); setdefaultcloseoperation(exit_on_close); setvisible(true); a jedyne, co dzieje się w kodzie, to dodanie pojedynczego komponentu typu Drawing (). Ponieważ JFrame domyślnie ma BorderLayout, ten składnik automatycznie wypełnia całe okno. Komponent jest zdefiniowany w następujący sposób: class Drawing extends JComponent

3 public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setstroke(new BasicStroke(7)); g2d.setcolor(color.gray); g2d.draw(new Line2D.Double(10, 400, 490, 400)); Line2D line = new Line2D.Double(20, 20, 50, 400); Rectangle2D rect = new Rectangle2D.Double(70, 20, 100, 380); g2d.setstroke(new BasicStroke(1)); g2d.setcolor(color.red); g2d.draw(line); g2d.setcolor(color.green); g2d.fill(rect); g2d.setcolor(color.blue); g2d.setfont(new Font("Liberation Sherif", Font.BOLD, 350)); g2d.drawstring("a", 200, 400); Jest to zatem niestandardowy komponent Swing. Składnik ma metodę o nazwie paintcomponent () i jest to metoda rysująca składnik. Parametr metody ma typ Grafika i jest to obiekt, który udostępnia dostępne narzędzia do rysowania i reprezentuje grafikę programu. Ten typ istniał przez cały czas, Java istnieje, ale wraz z wprowadzeniem Java2D zdefiniowano pochodną klasę o nazwie Graphics2D, która rozszerza się o nowe metody graficzne. Dlatego uruchamia metodę paintcomponent (), aby wpisać rzut parametru na obiekt Graphics2D. Następnie metoda może rysować na obiekcie graficznym i zasadniczo dzieje się tak. Poniższe instrukcje definiują rysowanie szarym piórem o szerokości 7 pikseli, a trzecie zdanie rysuje linię prostą między dwoma punktami: g2d.setstroke (nowy BasicStroke (7)); g2d.setcolor (Color.gray); g2d.draw (nowy Line2D.Double (10, 400, 490, 400)); Następne dwie instrukcje definiują dwa kształty geometryczne, które są odpowiednio linią i prostokątem: Line2D line = new Line2D.Double(20, 20, 50, 400); Rectangle2D rect = new Rectangle2D.Double(70, 20, 100, 380); Następne trzy instrukcje definiują rysowanie czerwonym piórem o wielkości 1 piksela, a trzecie zdanie rysuje powyższą linię prostą:

4 g2d.setstroke (nowy BasicStroke (1)); g2d.setcolor (Color.red); g2d.draw (line); Następne dwa stwierdzenia ponownie mówią, że należy pomalować na zielony kolor, a powyższy prostokąt jest wypełniony kolorem zielonym: g2d.setcolor (Color.green); g2d.fill (rect); Ostatnie trzy stwierdzenia mówią, że kolor powinien być niebieski, tekst należy narysować pogrubioną czcionką w 350 punktach i na koniec narysować dużą literę A: g2d.setcolor(color.blue); g2d.setfont(new Font("Liberation Sherif", Font.BOLD, 350)); g2d.drawstring("a", 200, 400); Oczywiście nie można wiedzieć, że stwierdzenia powinny być napisane w ten sposób, ale kiedy zobaczysz poszczególne stwierdzenia, są one wystarczająco łatwe do zrozumienia. ĆWICZENIE 1 Napisz program, który możesz wywołać Java2DShapes. Program powinien otworzyć okno, jak pokazano poniżej: Program można napisać w taki sam sposób jak powyżej. Trójkąt jest rysowany jako trzy linie proste. Spróbuj sam (korzystając z dokumentacji Java), aby dowiedzieć się, jak narysować okrąg. Typ to Ellipse2D.

5 1.1 KLASA GRAPHICS2D Zanim odniosę się do przykładów, powiem kilka słów o klasie Graphics2D, a tym samym nieco dotyczących techniki. Proces rysowania w oknie opiera się na wielu obiektach, takich jak kształty, obrazy, tekst itp., Które pozwalają określić, które z nich zabarwia poszczególne piksele na ekranie lub drukarce, a proces ten nazywa się renderowaniem, a zatem renderowanie to proces wyświetlania wyników prymitywów graficznych na urządzeniu wyjściowym. Klasa Graphics2D może być postrzegana jako silnik renderujący Java2D. Ponadto obiekt Graphics2D reprezentuje powierzchnię graficzną w postaci zbioru pikseli, z których każdy ma kolor. Sposób, w jaki obiekt konwertuje prymitywy graficzne na piksele, i to, jakie kolory otrzymują, są określane przez stan obiektu, który obejmuje siedem pojęć: 1. malować, która określa, które kolory mają być używane do rysowania i wypełniania kształtu, a także zawiera tekst 2. obrys, wskazujący grubość linii, za pomocą której kształty są rysowane za pomocą metody draw () 3. czcionka wskazująca sposób renderowania tekstu 4. transformacja określająca, w jaki sposób prymitywne obiekty graficzne powinny zostać przekształcone (przesunięte, obrócone, rozciągnięte) przed renderowaniem, w tym sposób, w jaki współrzędne logiczne muszą zostać przekonwertowane na współrzędne fizyczne (domyślnie 72 współrzędne logiczne są przekształcane na cal na urządzeniu fizycznym) 5. kompozycja stosowana do określania sposobu łączenia kolorów z istniejącym kolorem na powierzchni rysunkowej 6. obcinanie, które określa ograniczenia renderowanego obszaru (domyślnie jest to null, co oznacza całą powierzchnię rysunku) 7. wskazówki dotyczące renderowania, istnieją techniki określające sposób renderowania obiektów Istnieją cztery ogólne metody renderowania prymitywów geometrycznych: 1. fill (), który wypełnia wnętrze kształtu 2. draw (), który rysuje obwód kształtu 3. drawstring (), która rysuje tekst 4. drawimage (), który służy do wyświetlania obrazu Proces renderowania składa się z kilku etapów, które są zasadniczo niezależne od powyższych czterech metod i wykonywane w następującej kolejności: 1. transformacja 2. rasteryzacja 3. obcinanie 4. malować 5. komponowanie Kiedy dana postać musi zostać zrenderowana, na przykład na ekranie, jest to przybliżenie, a kształt niekoniecznie musi być dokładnie przedstawiony. Ekran reprezentuje kształt za pomocą kropek

6 (pikseli) i na przykład nie można utworzyć dokładnego okręgu z punktami (widać, że obrzeże składa się z punktów, a nie z łuku). Do określania punktów, które muszą być częścią określonego kształtu, komputer stosuje jeden z dwóch sposobów. Domyślnie jest to aliasing, w którym punkty, których środek znajduje się w obrębie figury, są kolorowe. Oznacza to, że poszczególne piksele są albo częścią rysunku, albo nie są. Rezultatem jest kształt, w którym krawędzie występują w piksele. Zaletą aliasingu jest to, że jest to skuteczny i szybki sposób na narysowanie kształtu, a w większości przypadków wynik jest w pełni zadowalający. Druga metoda nazywa się antyaliasingiem i tutaj komputer określa dla każdego piksela przecięcie figury i bieżącego piksela. Piksel jest zabarwiony nasyceniem określonym przez stopień, w jakim jest częścią figury. Rezultatem jest liczba, w której linie i łuki nie w tym samym stopniu występują w piksele. Na poniższym rysunku okrąg po lewej stronie jest renderowany za pomocą aliasingu, podczas gdy okrąg po prawej stronie jest renderowany za pomocą antyaliasingu. Zwróć także uwagę na różnicę między tekstem. Wadą antyaliasingu jest oczywiście to, że jest to kompleksowy proces renderowania, który należy przeprowadzić w wielu obliczeniach. Kod dla poniższego okna jest następujący, w którym należy przede wszystkim zauważyć, jak określić, że obiekt Graphics2D musi używać antyaliasingu: package aliasing; import java.awt.*; import java.awt.geom.ellipse2d; import javax.swing.*; public class MainWindow extends JFrame public MainWindow() super("aliasing og antialliasing");

7 setsize(500, 350); setlocationrelativeto(null); add(new Drawing()); setdefaultcloseoperation(exit_on_close); setvisible(true); class Drawing extends JComponent public void paintcomponent(graphics g) Ellipse2D cirkel1 = new Ellipse2D.Double(20, 20, 200, 200); Ellipse2D cirkel2 = new Ellipse2D.Double(260, 20, 200, 200); Graphics2D g2d = (Graphics2D)g; g2d.setstroke(new BasicStroke(5)); g2d.draw(cirkel1); g2d.setfont(new Font("Liberation Sherif", Font.PLAIN, 36)); g2d.drawstring("aliasing", 50, 270); g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.draw(cirkel2); g2d.drawstring("antialiasing", 250, 270); Powyżej wspomniałem, że proces renderowania obejmuje rasteryzację i komponowanie oraz wskazuje stopień, w jakim piksele postaci muszą pokrywać piksele powierzchni rysunkowej. Ta wartość nazywa się wartością alfa i jest liczbą od 0,0 (niewidoczna lub przezroczysta) do 1,0 (całkowicie zakryta lub nieprzezroczysta). Jako przykład pokazuje następujące okno dwie proste linie i trzy prostokąty, w których dwie linie są rysowane jako pierwsze:

8 Pierwszy prostokąt jest rysowany z wartością alfa 1, a zatem obejmowałby w całości piksele. W rezultacie dwie linie są pokryte prostokątem. Środkowy prostokąt ma wartość alfa ½, a zatem nie pokrywa całkowicie linii. W rezultacie czerwony kolor jest częściowo łączony z czarnymi liniami i szarym tłem. Ostatni prostokąt ma wartość alfa równą 0, a zatem jest całkowicie przezroczysty i dlatego nie jest widoczny. Kod jest następujący: package alphaprogram; import java.awt.*; import java.awt.geom.*; import javax.swing.*; public class MainWindow extends JFrame public MainWindow() super("alpha"); setsize(500, 300); setlocationrelativeto(null); add(new Drawing()); setdefaultcloseoperation(exit_on_close); setvisible(true);

9 class Drawing extends JComponent public void paintcomponent(graphics g) Dimension size = getsize(); Graphics2D g2d = (Graphics2D)g; g2d.setstroke(new BasicStroke(10)); g2d.setrenderinghint(renderinghints.key_antialiasing, RenderingHints.VALUE_ANTIALIAS_ON); Line2D line1 = new Line2D.Double(0, 0, size.width, size.height); Line2D line2 = new Line2D.Double(0, size.height, size.width, 0); double width = size.width / 7.0; Rectangle2D rect1 = new Rectangle2D.Double(width, 0, width, size.height); Rectangle2D rect2 = new Rectangle2D.Double(3 * width, 0, width, size.height); Rectangle2D rect3 = new Rectangle2D.Double(5 * width, 0, width, size.height); g2d.draw(line1); g2d.draw(line2); g2d.setcolor(color.red); g2d.fill(rect1); g2d.setcolor(new Color(1.0F, 0F, 0F, 0.5F)); g2d.fill(rect2); g2d.setcolor(new Color(1.0F, 0F, 0F, 0F)); g2d.fill(rect3); g2d.setstroke(new BasicStroke(1)); g2d.setcolor(color.black); g2d.draw(rect1); g2d.draw(rect2); g2d.draw(rect3); Jeśli przetestujesz program, zauważ, że zarówno linie, jak i prostokąty odpowiadają rozmiarowi okna. Podczas pracy z Javą 2D i ogólnie z grafiką komputerową masz układ współrzędnych. Lewy górny róg okna to (0,0), a oś x jest skierowana w prawo, a oś y jest skierowana w dół. Jest to logiczny układ współrzędnych zwany przestrzenią użytkownika, a gdy obiekty do narysowania na fizycznym

10 wyświetlaczu lub drukarce, masz inny układ współrzędnych, zwany przestrzenią urządzenia. Renderowanie wymaga zatem konwersji współrzędnych z przestrzeni użytkownika na współrzędne urządzenia, a dla ekranu ta konwersja jest zwykle wykonywana bezpośrednio, gdy 1 jednostka w przestrzeni użytkownika odpowiada jednemu pikselowi, ale ogólnie dąży do konwersji, w której 72 jednostki (w przestrzeni użytkownika) są przeliczone na 1 cal. ĆWICZENIE 2 Musisz napisać program (moje rozwiązanie nazywa się AlphaValue), który otwiera okno, jak pokazano powyżej, w którym jest pomalowany kwadrat i okrąg. U góry znajduje się suwak, a jeśli przesuniesz suwak, figura powinna być przykryta zielonym prostokątem, który ostatecznie całkowicie zakrywa figurę:

11 Należy powiązać moduł obsługi zdarzeń z suwakiem, który przerysowuje komponent po przesunięciu suwaka. Jeśli rysunek odnosi się do komponentu z figurami, moduł obsługi zdarzeń można zdefiniować jako obiekt następującej klasy wewnętrznej: private class ChangeHandler implements ChangeListener public void statechanged(changeevent e) drawing.repaint(); 2 KSZTAŁTY W tej części pokażę, jak definiować i rysować kształty geometryczne, co już zrobiłem z powyższymi przykładami, na przykład prostokątem. Jego typ to Rectangle2D, ale jest to klasa abstrakcyjna i istnieją dwie konkretne klasy pochodne, które nazywane są odpowiednio Rectangle2D.Float i Rectangle2D.Double. Różnica polega na atrybutach figury i miejscu, w którym są one zdefiniowane przez typ float lub type double. Ten wzór jest używany dla wszystkich prymitywnych obiektów Shape. Istnieje klasa pomocnicza, zwana Point2D (i Point2D.Float i Point2D.Double), która reprezentuje punkt. Obiekt Point2D nie ma rozmiaru i nie może być renderowany, a obiekt służy tylko do definiowania atrybutów dla innych obiektów. Podstawowe klasy geometryczne pokazano na poniższym rysunku i wszystkie są zdefiniowane przez wspólny interfejs o nazwie Kształt. Ten interfejs definiuje kilka ważnych metod, o których wspomnę - getbounds2d (), która zwraca prostokąt opisujący - zawiera (), który sprawdza, czy punkt lub prostokąt jest zawarty w kształcie - intersects (), który sprawdza, w którym prostokąt pokrywa się z bieżącym kształtem

12 2.1 WYPEŁNIONE KSZTAŁTY Cztery ostatnie są proste w użyciu, a różnica polega głównie na parametrach, które należy wprowadzić, aby utworzyć obiekt. Zasadniczo figura jest zdefiniowana przez prostokąt, czyli prostokąt opisujący lewy górny róg oraz szerokość i wysokość. Program Figures otwiera następujące okno: Jeśli chodzi o typy Rectangle2D i Ellipse2D, nie ma wiele więcej do powiedzenia, ale w przypadku RoundRectangle2D musisz poza opisanym rektangle określić sposób zaokrąglania narożników - w rzeczywistości pod względem szerokości i wysokości prostokąta. Następnie jest zielony kształt, którym jest Arc2D. Najpierw musisz zdefiniować prostokąt opisujący, a tutaj musisz pamiętać, że prostokąt jest prostokątem, który otacza elipsę, której kształt jest częścią. Oprócz prostokąta musisz określić kąt (w stopniach), od którego powinien zacząć się zakres i jak duży kąt powinien obejmować zakres. Na koniec musisz określić, jak plasterek powinien wyglądać w przypadku, gdy dostępne są następujące opcje: - Arc2D.PIE, który pokazuje kształt w tym przykładzie

13 - Arc2D.OPEN, który pokazuje tylko łuk - Arc2D.CHORD, który pokazuje łuk i akord punktów końcowych łuku Kod powyższego okna to: package figures; import java.awt.*; import java.awt.geom.*; import javax.swing.*; public class MainWindow extends JFrame public MainWindow() super("figures"); setsize(500, 300); setlocationrelativeto(null); add(new Drawing()); setdefaultcloseoperation(exit_on_close); setvisible(true); class Drawing extends JComponent public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; draw(g2d, new Rectangle2D.Double(20, 20, 200, 100), Color.blue); draw(g2d, new RoundRectangle2D.Double(240, 20, 200, 100, 50, 30), Color.yellow); draw(g2d, new Ellipse2D.Double(20, 140, 200, 100), Color.red); draw(g2d, new Arc2D.Double(240, 140, 200, 200, 30, 45, Arc2D.PIE), Color.green); private void draw(graphics2d g, Shape shape, Color color) g.setcolor(color); g.fill(shape); g.setcolor(color.black); g.draw(shape); ĆWICZENIE 3

14 Zrób kopię powyższego projektu, który otworzy okno z czterema cyframi. Musisz zmienić program, aby po kliknięciu kształtu pojawił się komunikat, jak pokazano poniżej, z informacją, która postać została kliknięta. Zauważ, że jedyne, czego potrzebujesz, to dodać moduł obsługi zdarzeń dla kliknięć myszą, który określa, czy figura została kliknięta, a jeśli tak, to co. 2.2 LINIE Następnie przyjrzę się trzem typom Line2D, QuadCurve2D i CubicCurve2D, z których każdy reprezentuje segment linii. Line2D reprezentuje linię prostą między dwoma punktami i nie ma wiele do wyjaśnienia. Aby utworzyć obiekt Line2D, musisz wskazać tylko punkty końcowe segmentu. QuadCurve2D reprezentuje ścieżkę między dwoma punktami, która ma powiązany punkt kontrolny, dzięki czemu linie od punktu kontrolnego punktów końcowych krzywej są styczne do krzywej: Jeśli chcesz utworzyć QuadCurve2D, musisz wskazać trzy punkty. CubicCurve2D jest w zasadzie taki sam, ale tutaj są dwa punkty kontrolne, po jednym dla każdego z dwóch punktów końcowych:

15 Dlatego należy określić cztery punkty, aby utworzyć CubicCurve2D. Ustalenie wyniku tych krzywych może być trochę trudne. Poniższy program otwiera okno, jak pokazano poniżej. Niebieska krzywa to CubicCurve2D, a czerwona to QuadCurve2D. Oprócz punktów końcowych rysunek pokazuje punkty kontrolne, a także styczne, a celem programu jest to, że możesz wskazać jednym z punktów za pomocą myszy i przeciągając go, i w ten sposób uzyskać pomysł co stanie się z kształtami, jeśli zmienisz punkty końcowe lub punkty kontrolne. Kod okna pokazano poniżej i musisz przede wszystkim obserwować, jak utworzyć odpowiednio QuadCurve2D i CubicCurve2D: package curves; mport java.awt.*; import java.awt.event.*; import java.awt.geom.*; import javax.swing.*; public class MainWindow extends JFrame public MainWindow() super("curves"); setsize(500, 500); setlocationrelativeto(null); add(new Drawing()); setdefaultcloseoperation(exit_on_close); setvisible(true); class Drawing extends JComponent private Point2D[] points = new Point2D.Double(50, 125), new Point2D.Double(100, 250), new Point2D.Double(375, 10), new Point2D.Double(450, 125), new Point2D.Double(50, 375), new Point2D.Double(300, 150), new Point2D.Double(450, 375) ; private Point2D current = null; public Drawing() addmouselistener(new ClickHandler()); addmousemotionlistener(new MoveHandler());

16 public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setcolor(color.gray); g2d.draw(new Line2D.Double(points[0], points[1])); g2d.draw(new Line2D.Double(points[2], points[3])); g2d.setcolor(color.blue); g2d.draw(new CubicCurve2D.Double(points[0].getX(), points[0].gety(), points[1].getx(), points[1].gety(), points[2].getx(), points[2].gety(), points[3].getx(), points[3].gety())); g2d.setcolor(color.gray); g2d.draw(new Line2D.Double(points[4], points[5])); g2d.draw(new Line2D.Double(points[5], points[6])); g2d.setcolor(color.red); g2d.draw(new QuadCurve2D.Double(points[4].getX(), points[4].gety(), points[5].getx(), points[5].gety(), points[6].getx(), points[6].gety())); for (int i = 0; i < points.length; ++i) g2d.setcolor(points[i].equals(current)? Color.magenta : Color.gray); g2d.fill(getpoint(points[i])); private Shape getpoint(point2d p) int side = 4; return new Rectangle2D.Double( p.getx() side / 2, p.gety() side / 2, side, side);

17 private class ClickHandler extends MouseAdapter public void mousepressed(mouseevent e) current = null; for (int i = 0; i < points.length; ++i) Shape shape = getpoint(points[i]); if (shape.contains(e.getpoint())) current = points[i]; break; repaint(); private class MoveHandler extends MouseMotionAdapter public void mousedragged(mouseevent e) if (current!= null) current.setlocation(e.getpoint()); repaint();

18 ĆWICZENIE 4 Utwórz kopię projektu Krzywe i zmodyfikuj program, tak aby dolna część okna pokazywała współrzędne myszy podczas przeciągania punktów, a dwie krzywe muszą zostać narysowane grubszą linią. Oznacza to, że musisz na przykład użyć obiektu Obrys podczas rysowania krzywych private Stroke stroke3 = new BasicStroke (3); Aby wyświetlić współrzędne myszy, możesz dodać komponent JLabel do dolnej części okna i aktualizować go za każdym razem, gdy przeciągniesz jeden z punktów.

19 ĆWICZENIE 5 Napisz program, który możesz wywołać StraightLines, który otworzy następujące okno pokazane poniżej. Suwak powinien reprezentować wartości od 0 do 100 i zaczynać się od 25. Okno musi narysować liczbę linii prostych wskazanych przez suwak i zmieniać na przemian sześć kolorów. Linia zaczyna się w lewym górnym rogu do punktu na dole okna, w którym dno jest podzielone na kilka punktów określonych jako suwak. Podobnie, linia zaczyna się w prawym dolnym rogu do punktu na lewej krawędzi, który jest podzielony na kilka punktów odpowiadających wartości suwaka. 2.3 OGÓLNA ŚCIEŻKA Line2D, QuadCurve2D, CubicCurve2D, Rectangle2D, RoundRectangle2D, Ellipse2D i Arc2D są podstawowymi prymitywami geometrycznymi w Java2D, ale z powyższego diagramu klas wynika, że istnieją dwie inne klasy i można ich użyć do zdefiniowania bardziej złożonych kształtów. Klasa GeneralPath definiuje kształt za pomocą pewnej liczby punktów, przy czym między każdym z tych punktów może znajdować się linia typu Line2D, QuadCurve2D lub CubicCurve2D. GeneralPath określa dowolny kształt, który niekoniecznie musi być zamknięty lub spójny. Możesz pomyśleć o kształcie w ten sposób, że narysujesz go piórem i możesz zrobić dwie rzeczy: 1. Możesz podnieść pióro i przesunąć je do punktu. 2. Możesz narysować linię od punktu, w którym znajduje się pióro, do innego punktu. W tym celu używasz metod: moveto(), lineto(), quadto(), cubicto() i closepath().

20 Poniższe okno pokazuje trzy kształty typu GeneralPath, w których czerwony składa się z trzech segmentów linii, segmentu poczwórnego i segmentu sześciennego. Dwa pozostałe kształty są utworzone wyłącznie przez segmenty linii: Zasadniczo wystarczy utworzyć ścieżkę GeneralPath i jest tylko jedna rzecz złożona, a mianowicie określenie wnętrza kształtów. Jeśli kształt jest ograniczony prostą zamkniętą krzywą (jak czerwony), nie ma wiele wątpliwości, ale jeśli kształt jest utworzony przez przecinające się linie, jest to trudniejsze i może się zdarzyć na dwa sposoby, które określasz za pomocą parametru do konstruktora. Jeśli weźmiesz pod uwagę niebieską cyfrę, zostanie ona zdefiniowana jako GeneralPath.WIND_EVEN_ODD. Linie proste dzielą kształt na kilka zamkniętych obszarów (w tym przypadku pięć). Jeśli musisz zdecydować, który z tych obszarów należy do figury, możesz narysować prostą linię, która przecina obszary figur: Jeśli zaczynasz poza kształtem i podąża za linią i za każdym razem, gdy mijasz segment, liczy się licznik w górę o 1, obowiązuje zasada, że obszar należy do figury, jeśli licznik jest nieparzysty i nie należy, jeśli licznik jest parzysty. Zielona cyfra jest zdefiniowana jako GeneralPath. WIND_NON_ZERO (która, nawiasem mówiąc, jest domyślna). Jeśli musisz tutaj ustalić, czy należy do figury, możesz podobnie narysować linię, która przecina obszary figury. Gdy linia przecina jeden z segmentów figury, reguła jest taka, że licznik jest zwiększany o 1, jeśli segment jest rysowany od lewej do prawej, a zmniejszany o 1, jeśli segment jest rysowany od prawej do lewej. Obszary, które są częścią figury, to obszary, w których licznik nie jest równy 0.

21 Kod programu to: package pathprogram; import java.awt.*; import java.awt.geom.*; import javax.swing.*; public class MainWindow extends JFrame public MainWindow() super("path"); setsize(800, 500); setlocationrelativeto(null); add(new Drawing()); setdefaultcloseoperation(exit_on_close); setvisible(true); class Drawing extends JComponent public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); GeneralPath path1 = new GeneralPath(); path1.moveto(50, 50); path1.lineto(250, 100);

22 path1.quadto(400, 200, 250, 300); path1.curveto(500, 300, 200, 400, 450, 450); path1.lineto(100, 400); path1.closepath(); g2d.setcolor(color.red); g2d.fill(path1); g2d.setcolor(color.black); g2d.draw(path1); GeneralPath path2 = new GeneralPath(GeneralPath.WIND_NON_ZERO); path2.moveto(500, 150); path2.lineto(650, 50); path2.lineto(600, 250); path2.lineto(500, 200); path2.lineto(700, 150); path2.lineto(620, 100); path2.lineto(580, 220); path2.closepath(); g2d.setcolor(color.green); g2d.fill(path2); g2d.setcolor(color.black); g2d.draw(path2); GeneralPath path3 = new GeneralPath(GeneralPath.WIND_EVEN_ODD); path3.moveto(500, 350); path3.lineto(650, 250); path3.lineto(600, 450); path3.lineto(500, 400); path3.lineto(700, 350); path3.lineto(620, 300); path3.lineto(580, 420); path3.closepath(); g2d.setcolor(color.blue); g2d.fill(path3); g2d.setcolor(color.black); g2d.draw(path3);

23 Oprócz tworzenia ścieżki ogólnej za pomocą segmentów można także utworzyć ścieżkę ogólną złożoną z innych kształtów. Poniższe okno pokazuje GeneralPath, która rysuje trójkąt, ale także składa się z innej GeneralPath (która również reprezentuje trójkąt) i Rectangle2D. Kliknięcie jednego z trzech kształtów powoduje wyświetlenie okna komunikatu, a celem jest pokazanie, że wszystkie trzy figury są częścią tego samego obiektu Shape. Kod jest następujący: package triangles; import java.awt.*; import java.awt.geom.*; import javax.swing.*; import java.awt.event.*; public class MainWindow extends JFrame public MainWindow() super("triangles"); setsize(500, 400); setlocationrelativeto(null);

24 add(new Drawing()); setdefaultcloseoperation(exit_on_close); setvisible(true); class Drawing extends JComponent private Shape shape; public Drawing() addmouselistener(new ClickHandler()); public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; GeneralPath path1 = new GeneralPath(); path1.moveto(50, 150); path1.lineto(250, 50); path1.lineto(100, 200); path1.closepath(); GeneralPath path = new GeneralPath(); path.moveto(200, 150); path.lineto(300, 50); path.lineto(400, 200); path.closepath(); path.append(path1, false); path.append(new Rectangle2D.Double(20, 250, 400, 50), false); g2d.setcolor(color.red); g2d.fill(path);

25 g2d.setcolor(color.black); g2d.draw(path); shape = path; private class ClickHandler extends MouseAdapter public void mouseclicked(mouseevent e) Point2D point = new Point2D.Double(e.getX(), e.gety()); if (shape.contains(point)) JOptionPane.showMessageDialog(Drawing.this, "You have clicked on the figure"); ĆWICZENIE 6 Napisz program, który możesz nazwać wielokątami, w którym musi otworzyć okno, jak pokazano poniżej (gdzie dolny kształt nie jest wielokątem). Wszystkie trzy kształty powinny być zdefiniowane jako ścieżka ogólna, w której dno jest zdefiniowane przez dwa segmenty poczwórne. Na koniec, jeśli klikniesz na jedną z trzech cyfr, powinieneś zobaczyć okno komunikatu informujące, który z kształtów klikasz:

26 2.4 OBSZAR Ostatnia klasa kształtów geometrycznych nazywa się Obszar. Jest to typ, który łączy jeden lub więcej kształtów, ale można określić, w jaki sposób powinny się one nakładać. Poniższe okno rysuje okrąg i kwadrat, które zachodzą na siebie. Liczby te służą do zdefiniowania czterech innych figur za pomocą obiektu Area. Tutaj powinieneś zauważyć cztery metody, które tworzą obiekty Area, ale także sposób ich umieszczenia w oknie poprzez translację obiektu graficznego. Odbywa się to z transformacją, która zostanie omówiona później, ale w tym przypadku liczby są umieszczane przez proste tłumaczenie. package areaprogram; import java.awt.*; import java.awt.geom.*; import javax.swing.*; public class MainWindow extends JFrame public MainWindow() super("area's"); setsize(500, 400); setlocationrelativeto(null); add(new Drawing()); setdefaultcloseoperation(exit_on_close); setvisible(true);

27 class Drawing extends JComponent public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Ellipse2D cirk = new Ellipse2D.Double(20, 20, 100, 100); Rectangle2D rect = new Rectangle2D.Double(70, 70, 100, 100); g2d.draw(cirk); g2d.draw(rect); union(g2d, cirk, rect, 170, 0); intersection(g2d, cirk, rect, 170, 0); subtraction(g2d, cirk, rect, -340, 170); exclusive(g2d, cirk, rect, 170, 0); private void union(graphics2d g, Shape shape1, Shape shape2, int x, int y) Area area1 = new Area(shape1); Area area2 = new Area(shape2); area1.add(area2); g.translate(x, y); g.setcolor(color.green); g.fill(area1); g.setcolor(color.black); g.draw(area1); private void intersection(graphics2d g, Shape shape1, Shape shape2, int x, int y) Area area1 = new Area(shape1); Area area2 = new Area(shape2);

28 area1.intersect(area2); g.translate(x, y); g.setcolor(color.blue); g.fill(area1); g.setcolor(color.black); g.draw(area1); private void subtraction(graphics2d g, Shape shape1, Shape shape2, int x, int y) Area area1 = new Area(shape1); Area area2 = new Area(shape2); area1.subtract(area2); g.translate(x, y); g.setcolor(color.yellow); g.fill(area1); g.setcolor(color.black); g.draw(area1); private void exclusive(graphics2d g, Shape shape1, Shape shape2, int x, int y) Area area1 = new Area(shape1); Area area2 = new Area(shape2); area1.exclusiveor(area2); g.translate(x, y); g.setcolor(color.red); g.fill(area1); g.setcolor(color.black); g.draw(area1);

29 3 WYPEŁNIANIE I POCIĄGNIĘCIA W poprzednim rozdziale opisałem, jak Java2D definiuje kształty geometryczne i jak narysować je w oknie. Narysować kształt obejmuje 1. w jaki sposób należy narysować obwód figury 2. w jaki sposób należy wypełnić wnętrze figury (jeśli jest wnętrze) i oba są przedmiotem tej części. Jeśli masz zamknięty kształt, taki jak prostokąt, możesz narysować jego obwód za pomocą funkcji draw () i możesz wypełnić ten kształt funkcją fill (). W oknie poniżej narysowane są dwa prostokąty Celem jest pokazanie, jak rysowany jest obwód. Pierwszy prostokąt jest rysowany w następujący sposób: Rectangle2D rect1 = nowy Rectangle2D.Double (20, 20, 200, 100); g2d.setpaint (Color.black); g2d.draw (rect1);

30 g2d.setpaint (kolor. żółty); g2d.fill (rect1); Należy zauważyć, że najpierw rysuję obwód, a następnie wypełnia prostokąt kolorem żółtym. W rezultacie obwód jest nadpisywany u góry i po lewej stronie, podczas gdy jest widoczny po prawej iu dołu. Podczas rysowania ramki rysowane są 4 linie proste: - linia od (20, 20) do (220, 20), czyli górna krawędź - linia od (20, 20) do (20, 120), czyli lewa krawędź - linia od (20, 120) do (220, 120), czyli dolna krawędź - linia od (220, 20) do (220, 120), czyli prawa krawędź Po wypełnieniu prostokąta obszar, w którym lewy górny róg to (20, 20), a prawy dolny róg (219, 119), jest kolorowy. Ważne jest, aby zdawać sobie sprawę z tego faktu, a ogólną zasadą jest, że przed narysowaniem obwodu musisz wypełnić rysunek. Należy zauważyć, że I w powyższym programie zdefiniowałem kolor za pomocą setpaint () zamiast setcolor (). W tym przypadku nie ma tego szczególnego powodu, ale klasa Graphics2D ma zmienną typu Paint, reprezentującą sposób, w jaki kolor lub kształt powinien zostać pokolorowany. Istnieją trzy opcje: 1. kolor 2. a GradientPaint 3. TexturePaint Trzy opcje są przedstawione poniżej, ale Paint jest interfejsem implementowanym przez klasę Color, dlatego można użyć setpaint () zamiast setcolor (). Drugi kształt w oknie poniżej jest rysowany w następujący sposób: Rectangle2D rect2 = nowy Rectangle2D.Double (240, 20, 200, 100); Ellipse2D ellip1 = nowy Ellipse2D.Double (238, 18, 5, 5); Ellipse2D ellip2 = nowy Ellipse2D.Double (438, 118, 5, 5); g2d.setpaint (Color.orange); g2d.fill (rect2); g2d.setpaint (Color.black); g2d.draw (rect2); g2d.setpaint (Color.black);

31 g2d.fill (ellip1); g2d.fill (ellip2); i powinien ponownie zilustrować, gdzie narysowana jest krawędź. Aby wyjaśnić dwa rogi prostokąta, narysowałem dwa kółka, z których oba mają średnicę 5. Pierwszy ma w środku (240, 20), a więc w lewym górnym rogu prostokąta. Drugi okrąg ma środek w (440, 120), a zatem w prawym dolnym rogu prostokąta określającego kształt. 3.1 GRADIENTPAINT Możesz również wypełnić kształt (a nawet narysować linię) GradientPaint. Tutaj mieszasz dwa kolory wzdłuż linii prostej. Rozważ następujące okno: pokazano trzy kwadraty z linią prostą. Linie proste są rysowane tylko w celu wyjaśnienia zasady. Pierwsza cyfra miesza kolory czerwony i żółty wzdłuż wyświetlanej linii, tak że zaczynasz od czerwonego w lewym górnym rogu, a kończysz na żółtym w prawym dolnym rogu. Środkowa figura miesza podobne kolory czerwony i żółty, ale tym razem linia nie rozciąga się na kwadrat otworu. Rezultat jest taki, że wszystko przed początkiem linii jest czerwone, a wszystko po linii jest żółte. Ostateczny kształt jest zasadniczo podobny, ale tym razem wzór się powtarza. Składnikiem reprezentującym trzy kwadraty są: class Drawing extends JComponent public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Rectangle2D rect1 = new Rectangle2D.Double(20, 20, 200, 200); Rectangle2D rect2 = new Rectangle2D.Double(240, 20, 200, 200); Rectangle2D rect3 = new Rectangle2D.Double(460, 20, 200, 200); g2d.setpaint(new GradientPaint(20, 20, Color.red, 220, 220, Color.yellow)); g2d.fill(rect1);

32 g2d.setpaint(color.black); g2d.draw(rect1); g2d.draw(new Line2D.Double(20, 20, 220, 220)); g2d.setpaint(new GradientPaint(310, 90, Color.red, 370, 150, Color.yellow)); g2d.fill(rect2); g2d.setpaint(color.black); g2d.draw(rect2); g2d.draw(new Line2D.Double(310, 90, 370, 150)); g2d.setpaint(new GradientPaint(530, 90, Color.red, 590, 150, Color.yellow, true)); g2d.fill(rect3); g2d.setpaint(color.black); g2d.draw(rect3); g2d.draw(new Line2D.Double(530, 90, 590, 150)); ĆWICZENIE 7 Napisz program, który możesz wywoływać Kręgi, który maluje okno, jak pokazano powyżej. Oto lewa elipsa jest wypełniona farbą GradientPaint, która miesza kolory jasnoszary i ciemnoszary wzdłuż średnicy poziomej, podczas gdy prawa elipsa miesza kolory niebieski i biały wzdłuż średnicy pionowej. Dwie elipsy muszą być zgodne z rozmiarem okna i zmieniać się, gdy zmienia się rozmiar okna. ĆWICZENIE 8 W tym ćwiczeniu musisz napisać program, możesz wywołać GradientBackground, który otworzy następujące okno:

33 Okno pokazuje prostokąt (zgodny z rozmiarem okna), który jest wypełniony GradientPaint, który miesza biel i czerń wzdłuż przekątnej od lewego górnego rogu. Okno ma trzy komponenty JSlider odpowiednio u góry iu dołu. Służą do określania odpowiednio białego i czarnego koloru. Po przesunięciu suwaków wynik może być na przykład taki, jak pokazano poniżej. Zauważ, że gdy komponent reprezentujący prostokąt musi odnosić się do sześciu komponentów JSlider, możesz ułatwić pracę, definiując Rysunek klasy jako klasę wewnętrzną w MainWindow. 3.2 TEKSTUREPAINT Możliwe jest również wypełnienie obrazu obrazem. Poniższy program rysuje prostokąt i wypełnia całe okno, a prostokąt jest wypełniony powtórzeniem ikony o wymiarach 64 x 64 pikseli:

34 Projekt ma podpakiet o nazwie obrazy i zawiera ikonę (plik png, który jest tutaj kopiowany, więc obraz jest zasobem i jest pakowany z klasami programu). Kod okna to: package texturebackground; import java.awt.*; import java.awt.image.*; import java.awt.geom.*; import javax.swing.*; public class MainWindow extends JFrame public MainWindow() super("texturebackground"); setsize(600, 400); setlocationrelativeto(null); add(new Drawing()); setdefaultcloseoperation(exit_on_close); setvisible(true);

35 class Drawing extends JComponent public void paintcomponent(graphics g) Dimension d = getsize(); Graphics2D g2d = (Graphics2D)g; Rectangle2D rect = new Rectangle2D.Double(0, 0, d.width, d.height); BufferedImage image = createimage(); g2d.setpaint( new TexturePaint(image, new Rectangle2D.Double(0, 0, image.getwidth(), image.getheight()))); g2d.fill(rect); public BufferedImage createimage() java.net.url imgurl = Drawing.class.getResource("/texturebackground/images/Bean.png"); ImageIcon icon = new ImageIcon(new ImageIcon(imgURL, ""). getimage().getscaledinstance(64, 64, Image.SCALE_SMOOTH), ""); BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.geticonheight(), BufferedImage.TYPE_INT_RGB); Graphics g = image.creategraphics(); icon.painticon(null, g, 0,0); g.dispose(); return image;

36 Metoda createimage () tworzy BufferedImage o wymiarach 20 x 20 pikseli, w którym narysowane jest czerwone kółko. Możesz tworzyć i manipulować BufferedeImage bez ładowania obrazu z pliku. BufferedImage jest tworzony w paintcomponent (), aby utworzyć TexturePaint, a następnie narysować okrąg pociągnięciem na 60 pikselach. Oprócz pokazania, jak utworzyć BufferedImage od zera, przykład pokazuje również, że obwód samej figury jest kształtem, który można wypełnić (za pomocą metody draw ()) obiektem Paint. 3.3 POCIĄGNIĘCIA W powyższych przykładach pokazałem, jak zdefiniować obiekt BasicStroke, a tym samym wskazać, jak gruba ma być narysowana linia. Domyślnie jest to 1. Istnieją jednak dwie rzeczy dotyczące pociągnięć, musisz mieć wiedzę na temat: - jakie muszą być punkty końcowe linii - w jaki sposób łączone są segmenty linii, a tym samym, jakie muszą być narożniki gdzie te koncepcje mają znaczenie tylko wtedy, gdy trzeba narysować grube linie. Wreszcie trzeba wiedzieć, jak narysować linię kropkowaną. Poniższe okno pokazuje większość dostępnych opcji:

37 Jeśli chodzi o punkty końcowe linii, istnieją trzy możliwości: 1. BasicStroke.CAP_SQUARE (domyślnie) 2. BasicStroke.CAP_ROUND 3. BasicStroke.CAP_BUTT Pierwsza linia (czarna) jest rysowana z wartościami domyślnymi, a w przeciwnym razie ma obrys o wartości 19. Ponadto jest rysowany prostokąt, który jest prostokątem opisującym linię. Jeśli chodzi o punkty końcowe, styl to CAP_SQUARE, który rozszerza linię o prostokąt, którego długość wynosi połowę grubości linii. Niebieska linia ma punkt końcowy CAP_ROUND, który rozszerza linię o półkole, którego promień wynosi połowę grubości linii. Wreszcie, istnieje CAP_BUTT, który jest używany przez zieloną linię i który po prostu nie rozwija linii. Trzy cyfry po prawej mają wszystkie typy GeneralPath i są narysowane za pomocą obrysu 19, a opcja CAP_SQUARE dla punktów końcowych, a także jest zdefiniowany sposób rysowania narożników. Ponownie istnieją trzy opcje: 1. JOIN_MITER (domyślnie) 2. JOIN_ROUND 3. JOIN_BEVEL

38 Pomarańczowa ścieżka jest rysowana z wartościami domyślnymi, a zatem stylem JOIN_MITER. Łączy segmenty, przedłużając zewnętrzne krawędzie, aż się przecinają. Linia karmazynowa używa JOIN_ ROUND, co oznacza po prostu, że punkty końcowe segmentów linii używają CAP_ROUND. Wreszcie JOIN_BEVEL jest używany przez szarą ścieżkę. Tutaj punkty końcowe segmentów linii używają CAP_BUTT, a zewnętrzne krawędzie są połączone. Następnie są trzy przerywane linie. Kolor czerwony jest rysowany za pomocą obrysu zdefiniowanego następująco: new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] 10, 10, 0) Linia przerywana jest rysowana przez przełączanie między segmentem linii a pustym segmentem. Ostatnia tablica określa, że każda z dwóch części powinna mieć długość 10. Ostatni parametr wyjaśniono w następnym przykładzie (linia różowa): new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] 10, 5, 5) Tutaj ostatni parametr wskazuje, jak długo trzeba zaczynać w pierwszym segmencie. Dlatego pierwszy segment ma tylko długość 5. Wreszcie ostatni przykład pokazuje, że można określić wiele długości segmentów: new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] 5, 10, 15, 20, 0) Oznacza to, że najpierw narysuj odcinek linii na 5, następnie odstęp 10, odcinek 15, a na końcu odstęp 20. Następnie powtarzaj wzór, aż narysowana zostanie cała linia. Kod komponentu pokazano poniżej: class Drawing extends JComponent public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); draw(g2d, 20, 20, 120, 70, new BasicStroke(19), Color.black, true); draw(g2d, 20, 120, 120, 170, new BasicStroke(19, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL), Color.blue, true); draw(g2d, 20, 220, 120, 270, new BasicStroke(19, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL), Color.green, true); draw(g2d, 200, 70, 300, 20, 400, 70, new BasicStroke(19), Color.orange); draw(g2d, 200, 170, 300, 120, 400, 170, new BasicStroke(19, BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND), Color.magenta); draw(g2d, 200, 270, 300, 220, 400, 270,

39 new BasicStroke(19, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL), Color.gray); draw(g2d, 20, 320, 400, 320, new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new fl 10, 10, 0), Color.red, false); draw(g2d, 20, 350, 400, 350, new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new fl 10, 5, 5), Color.pink, false); draw(g2d, 20, 380, 400, 380, new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] 5, 10, 15, 20, 0), Color.darkGray, false); private void draw(graphics2d g, double x1, double y1, double x2, double y2, Stroke stroke, Color color, boolean showbound) g.setstroke(stroke); g.setpaint(color); Line2D line = new Line2D.Double(x1, y1, x2, y2); g.draw(line); if (showbound) g.setstroke(new BasicStroke(0.5F)); g.draw(line.getbounds2d()); private void draw(graphics2d g, double x1, double y1, double x2, double y2, double x3, double y3, Stroke stroke, Color color) g.setstroke(stroke); g.setpaint(color); GeneralPath path = new GeneralPath(); path.moveto(x1, y1); path.lineto(x2, y2); path.lineto(x3, y3); g.draw(path);

40 g.setstroke(new BasicStroke(0.5F)); g.draw(path.getbounds2d()); ĆWICZENIE 9 Napisz program, który otworzy następujące okno: 4 RENDEROWANIE W poprzednim rozdziale omówiłem definiowanie kształtów geometrycznych. W tym rozdziale przyjrzę się, jak Java2D renderuje liczby. Oto jak follinging: 1. transformacja 2. komponowanie 3. obcinanie 4. renderowanie wskazówek 4.1 TRANSFORMACJE Transformacja jest zdefiniowana przez klasę o nazwie AffineTransform, a transformacja afiniczna to transformacja, która zachowuje linie równoległe. Klasa Graphics2D ma instancję AffineTransform

41 mającą zastosowanie do wszystkich kształtów geometrycznych podczas renderowania. Transfomację można zmienić na dwa sposoby. Można go częściowo zastąpić innym: void settransform (AffineTransform tr) i możesz zmienić istniejącą transformację void transform (AffineTransform Tx) Ogólnie zaleca się stosowanie tego drugiego podejścia, a przykłady pokazują, w jaki sposób. Klasa Graphics2D ma również metody, które bezpośrednio wykonują proste transformacje. Istnieją cztery rodzaje transformacji - tłumaczenie (występuje przesunięcie równoległe) - obrót (który obraca figurę wokół punktu) - skalowanie (które skaluje postać) - ścinanie (ścinające figurę) Tłumaczenie Program TranslateDemo otwiera okno, jak pokazano poniżej. Okno rysuje 4 prostokąty i najpierw rysuje czarny prostokąt. Pozostałe trzy są tym samym prostokątem, ale są rysowane po równoległym przesunięciu grafiki (powierzchni rysowania). Natychmiast jest to proste, ale jeśli wykonałeś kilka transformacji (jak w tym przypadku), wynik może być trudny do uchwycenia. Kod komponentu jest następujący i powinien również pokazywać, że przesunięcie równoległe można zdefiniować na kilka sposobów: class Drawing extends JComponent public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Rectangle2D rect = new Rectangle2D.Double(20, 20, 200, 100); g2d.draw(rect); AffineTransform start = g2d.gettransform();

42 g2d.translate(100, 50); g2d.setpaint(color.red); g2d.draw(rect); AffineTransform trans = g2d.gettransform(); trans.translate(100, 50); g2d.transform(trans); g2d.setpaint(color.blue); g2d.draw(rect); g2d.settransform(start); AffineTransform translate = AffineTransform.getTranslateInstance(50, 200); g2d.transform(translate); g2d.setpaint(color.green); g2d.draw(rect); Najpierw jest zdefiniowany prostokąt prostokątny, który jest rysowany przy ustawieniach domyślnych, a wynikiem jest czarny prostokąt. Następnie dodawany jest początek odniesienia dla bieżącej transformacji. Celem jest umożliwienie przywrócenia domyślnej transformacji powierzchni rysunkowej po wykonaniu innych transformacji. Następnie metoda wykonuje instrukcję g2d.translate (100, 50); i prostokąt jest narysowany kolorem czerwonym. Jest to przesunięcie równoległe 100 w kierunku osi x i 50 w kierunku osi y. Oznacza to, że punkt początkowy układu współrzędnych (lewy górny róg) jest przesunięty do (100, 50). Należy zauważyć, że po wykonaniu tej metody AffineTransform obiektu

43 Graphics2D ulega zmianie. Gdy definiowany jest następny krok, odniesienie trans do bieżącego AffineTransform, a następnie przesunięcie równoległe do (100, 50), które służy do modyfikacji transformacji obiektu g2d, a prostokąt jest rysowany ponownie, ale tym razem z kolorem niebieskim. Tutaj wynik nie jest całkowicie oczywisty, ponieważ jest to transformacja złożona. Najpierw wykonuje równoległy (100, 50), który przenosi bieżący punkt początkowy (100, 50) do (200, 100). Następnie pierwsza transformacja, która jest również przesunięciem równoległym (100, 50), która przenosi punkt początkowy (200,100) do (300, 150). W rezultacie niebieski prostokąt ma lewy górny róg w (320,170). Po narysowaniu niebieskiego prostokąta transformacja jest resetowana do domyślnej i definiowane jest nowe przesunięcie równoległe przy użyciu metody statycznej w klasie AffineTransform. Wyniki w zielonym prostokącie. ĆWICZENIE 10 Napisz program, który utworzy następujące okno: kiedy niebieski i zielony okrąg muszą być przekształceniami (przesunięciami równoległymi) żółtego. Obrót Obroty działają w zasadzie tak samo jak przesunięcie równoległe i można je również zdefiniować na trzy różne sposoby. W poniższym przykładzie użyłem tylko jednego sposobu, ale dwie pozostałe metody działają tak samo, jak przy przesunięciach równoległych. Obrót powoduje obrócenie układu współrzędnych, a tym samym powierzchni do rysowania wokół punktu. Domyślnie jest to punkt (0, 0), ale można również obracać wokół dowolnego innego punktu, a wynikiem jest przesunięcie równoległe, po którym następuje obrót. Obrót jest określony przez kąt zmierzony w radianach.

44 class Drawing extends JComponent public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Rectangle2D rect1 = new Rectangle2D.Double(0, 0, 200, 100); Rectangle2D rect2 = new Rectangle2D.Double(220, g2d.setpaint(color.darkgray); g2d.draw(rect1); g2d.draw(rect2); AffineTransform start = g2d.gettransform(); g2d.rotate(math.pi / 6); g2d.setpaint(color.blue); g2d.draw(rect1); g2d.settransform(start); g2d.rotate(-math.pi / 6, 200, 100);

45 g2d.setpaint(color.red); g2d.draw(rect1); g2d.settransform(start); g2d.rotate(-math.pi / 2, 420, 220); g2d.setpaint(color.magenta); g2d.draw(rect2); Skalowanie Jest to transformacja, w której zmienia się rozmiar figury - zarówno w poziomie, jak i w pionie. Poniższy przykład skaluje prostokąt: class Drawing extends JComponent public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Rectangle2D rect = new Rectangle2D.Double(170, 70, 200, 100); g2d.setpaint(color.darkgray); g2d.draw(rect); AffineTransform start = g2d.gettransform(); g2d.scale(2, 2); g2d.setpaint(color.blue);

46 g2d.draw(rect); g2d.settransform(start); g2d.scale(0.5, 0.5); g2d.setpaint(color.magenta); g2d.draw(rect); g2d.settransform(start); g2d.scale(1, 0.5); g2d.setpaint(color.red); g2d.draw(rect); Prostokąt, który jest skalowany, to czarny prostokąt. Należy pamiętać, że podczas skalowania figury zmieniany jest zarówno kształt, jak i lokalizacja figur. Zmiana lokalizacji jest spowodowana skalowaniem wszystkich odległości od punktu początkowego układu współrzędnych. Lewy górny róg czarnego prostokąta to (170, 70), a niebieski prostokąt ma lewy górny róg na (340, 140) i szerokość 400 i wysokość 200. Podobnie lewy górny róg współrzędnych magenta prostokąta wynosi (85, 35), a szerokość 100 i wysokość 50, a czerwony prostokąt jest jedyną skalą w pionie, a jego lewy górny róg to (170, 35). Szerokość pozostaje niezmieniona 200, a wysokość 50. ĆWICZENIE 11 Utwórz kopię projektu ScaleDemo (program powyżej). Musisz zmodyfikować program, aby całe skalowanie było oparte na lewym górnym rogu czarnego prostokąta. Rozciąganie Ostatnia z podstawowych transformacji nazywana jest ścinaniem, w którym oś X, oś Y lub obie osie są rozciągnięte. Poniższe okno pokazuje, jak rozciągnąć prostokąt (czarny prostokąt). We wszystkich trzech przypadkach transformacja jest równoległym przesunięciem prostokąta z lewym górnym rogiem w (0, 0), po którym następuje transformacja ścinająca. Oznacza to, że wszystkie trzy transformacje są transformacjami złożonymi. Zasadniczo tworzenie transformacji jest dość proste, ale wynik może być trudny do zrozumienia, a kolejność ma znaczenie. class Drawing extends JComponent public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Rectangle2D rect = new Rectangle2D.Double(0, 0, 200, 100); AffineTransform start = g2d.gettransform(); g2d.translate(20, 20);

47 g2d.setpaint(color.darkgray); g2d.draw(rect); g2d.shear(1, 0); g2d.setpaint(color.blue); g2d.draw(rect); g2d.settransform(start); g2d.translate(20, 140); g2d.setpaint(color.darkgray); g2d.draw(rect); g2d.setpaint(color.green); g2d.shear(0, 0.3); g2d.draw(rect); g2d.settransform(start); g2d.translate(340, 20); g2d.setpaint(color.darkgray); g2d.draw(rect); g2d.setpaint(color.red); g2d.shear(1, 0.3); g2d.draw(rect); 4.2 SKŁADANIE

48 Kompozycja odnosi się do procesu łączenia dwóch obrazów i pojawia się przy każdym narysowaniu kształtu lub obrazu. Graphics2D reprezentuje, jak wspomniano, powierzchnię rysunkową i za każdym razem, gdy rysujesz na niej kształt, tekst lub obraz, dodaje to nowy element do powierzchni rysunkowej. Odbywa się to zasadniczo przy użyciu trzech operacji: 1. Rasteryzacja produktów na podstawie bieżących wartości alfa wartości, które można traktować jako siatkę pikseli, z których każda ma wartość alfa. Zasadniczo ta siatka odpowiada całej powierzchni do rysowania i jest opisana poniżej jako źródło. Natomiast powierzchnia rysunku jest nazywana miejscem docelowym. 2. Dla każdego piksela w źródle określa się kolor na podstawie prądu Maluj obiekt używany przez obiekt Graphics2D lub obraz. 3. Źródło i miejsce docelowe (kolor i wartość alfa) są łączone piksel po pikselu, aby utworzyć obraz na powierzchni do rysowania. Kombinacja źródła i miejsca docelowego odbywa się na podstawie reguły składania, która dokładnie określa sposób użycia wartości koloru i alfa. Jeśli na przykład spojrzysz na to, co się stanie, gdy czarna figura zostanie renderowana na białej powierzchni rysunkowej i przy użyciu domyślnej reguły komponowania, możesz opisać ten proces w następujący sposób. Rasteryzacja przekształca liczbę na tablicę wartości alfa, gdzie piksele całkowicie poza kształtem otrzymują wartość 0,0 (interpretowaną jako przezroczysta), a odpowiednie piksele w miejscu docelowym pozostaną niezmienione. Podobnie piksele we wnętrzu rysunku mają wartość alfa 1,0, a odpowiadające im piksele w miejscu docelowym otrzymają ten sam kolor co piksele sourcens, a w tym przypadku wynikiem będzie kolor czarny. Następnie są piksele, które znajdują się na krawędzi, które otrzymują wartość alfa pomiędzy 0.0 i 1.0 (przy założeniu użycia antyaliasingu). Mają kolor będący połączeniem kolorów pikseli źródła i miejsca docelowego, w tym przypadku będzie to połączenie czerni i bieli, a tym samym odcienia szarości. Dokładny kolor zależy od wartości alfa w źródle. Na przykład piksel o wartości alfa 0,9 daje bardzo ciemnoszary kolor. Powyższe określa się jako domyślną regułę kompozytu, ale w szczególnych przypadkach można użyć innych. Nazywa się je regułami Porter-Duff, ale nie są tutaj omawiane. 4.3 WYCINANIE Czasami nie jest zainteresowany, abyś mógł rysować na całym obszarze rysowania. Podczas budowania skomplikowanych kształtów często trzeba upewnić się, że nie rysujesz na innych figurach, i można to zapewnić, określając obszar klipu. Rozważ następujące okno, które pochodzi z wcześniejszego przykładu:

49 Różnica polega tylko na tym, że dodano przycisk w górnej części okna. Po kliknięciu przycisku okno jest przerysowywane, a wynik jest pokazany poniżej, co rysuje trójkąt. Odbywa się to poprzez zdefiniowanie obszaru klipu jako trójkąta i przerysowana jest tylko ta część komponentu. Kliknij przycisk jeden raz, wynikiem jest elipsa, ponieważ teraz zdefiniowano obszar klipu jest elipsą. Kliknięcie po raz trzeci powoduje powrót do początku. Definiowanie obszaru klipu jest proste, jak pokazuje poniższy kod, i należy pamiętać, że obszar może mieć dowolny kształt:

50 package clipdemo; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.awt.geom.*; import javax.swing.*; public class MainWindow extends JFrame private JButton cmdclip = new JButton("Klip"); private int state = 0; public MainWindow() super("clipping"); setsize(650, 370); setlocationrelativeto(null); add(cmdclip, BorderLayout.NORTH); add(new Drawing()); cmdclip.addactionlistener(new ClickHandler()); setdefaultcloseoperation(exit_on_close);

51 setvisible(true); class ClickHandler implements ActionListener public void actionperformed(actionevent e) state = (state + 1) % 3; repaint(); class Drawing extends JComponent public void paintcomponent(graphics g) Dimension d = getsize(); Graphics2D g2d = (Graphics2D)g; Rectangle2D rect = new Rectangle2D.Double(0, 0, d.width, d.height); g2d.setpaint(color.white); g2d.fill(rect); BufferedImage image = createimage(); g2d.setpaint(new TexturePaint(image, new Rectangle2D.Double(0, 0, image.getwidth(), image.getheight()))); if (state == 1) GeneralPath path = new GeneralPath(); path.moveto(d.width / 2, 50); path.lineto(d.width 50, d.height 50); path.lineto(50, d.height 50); path.closepath(); g2d.setclip(path); else if (state == 2)

52 Ellipse2D ellip = new Ellipse2D.Double(50, 50, d.width 100, d.height 100); g2d.setclip(ellip); g2d.fill(rect); public BufferedImage createimage() java.net.url imgurl = Drawing.class.getResource("/clipdemo/images/Bean.png"); ImageIcon icon = new ImageIcon(new ImageIcon(imgURL, ""). getimage().getscaledinstance(64, 64, Image.SCALE_SMOOTH), ""); BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.geticonheight(), BufferedImage.TYPE_INT_RGB); Graphics g = image.creategraphics(); icon.painticon(null, g, 0,0); g.dispose(); return image; 4.4 WSKAZÓWKI RENEROWANIA Jako ostatnią rzecz w tym rozdziale wspomniałem o renderowaniu wskazówek, jak już wspomniałem o wygładzaniu krawędzi. Jest to okazja do sprawdzenia jakości rysowanej figury. Mechanizm renderowania Graphics2D może robić większość rzeczy na więcej niż jeden sposób, a wskazówki dotyczące renderowania są takie, że programiści chcą szacunku do jakości, ale nie jest pewne, czy mechanizm może zapewnić pożądaną jakość. Można zdefiniować wskazówki renderowania jako pary klucz / wartość, a możliwości pokazano w poniższej tabeli: Klucz KEY_ANTIALIASING Wartość VALUE_ANTIALIAS_ON VALUE_ANTIALIAS_OFF VALUE_ANTIALIAS_DEFAULT

53 KEY_RENDERING VALUE_RENDER_QUALITY VALUE_RENDER_SPEED VALUE_RENDER_DEFAULT KEY_DITHERING VALUE_DITHER_DISABLE VALUE_DITHER_ENABLE VALUE_DITHER_DEFAULT KEY_COLOR_RENDERING VALUE_COLOR_RENDER_QUALITY VALUE_COLOR_RENDER_SPEED VALUE_COLOR_RENDER_DEFAULT KEY_FRACTIONALMETRICS VALUE_FRACTIONALMETRICS_ON VALUE_FRACTIONALMETRICS_OFF VALUE_FRACTIONALMETRICS_DEFAULT KEY_TEXT_ANTIALIASING VALUE_TEXT_ANTIALIAS_ON VALUE_TEXT_ANTIALIAS_OFF VALUE_TEXT_ANTIALIAS_DEFAULT KEY_INTERPOLATION VALUE_INTERPOLATION_BICUBIC VALUE_INTERPOLATION_BILINEAR VALUE_INTERPOLATION_NEAREST_NEIGHBOR KEY_ALPHA_INTERPOLATION VALUE_ALPHA_INTERPOLATION_QUALITY VALUE_ALPHA_INTERPOLATION_SPEED VALUE_ALPHA_INTERPOLATION_DEFAULT Do tej pory użyłem tylko g2d.setrenderinghint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); ale później użyję innych. 5 TEKST Trzeba także narysować tekst, a nawet jeśli zwykle współpracuje się z tekstem w odniesieniu do komponentów Swing, w których nie trzeba nic robić, to tekst musi być również narysowany gdzieś, zadanie, którym Java2D musi się zająć. W rzeczywistości dołączono wiele szczegółów do tekstu, który jest przedmiotem tego rozdziału, ale w zasadzie tekstowymi obiektami graficznymi można manipulować w taki sam sposób, jak innymi obiektami graficznymi i kształtami.

54 Tekst zawiera znaki zakodowane jako Unicode, dlatego też dla narysowania tekstu dla każdego znaku należy znaleźć i wyrenderować postać reprezentującą znak. Taki rysunek nazywa się glifem, a czcionka zawiera glify dla wszystkich znaków obsługiwanych przez czcionkę. Do czcionki dołączone są metryki czcionki wskazujące rozmiary każdego znaku. Na wysokość są trzy rozmiary. Znaki są rysowane na linii podstawowej (znaki stoją na linii podstawowej). Zejście wskazuje, ile znaków muszą wypełnić poniżej linii bazowej i wynurz się, ile mogą wypełnić powyżej linii bazowej. Na koniec określ wiodącą ilość odstępu między wierszami, a trzy rozmiary razem nazywane są wysokością czcionki. Jako przykład wszystkiego, co możesz rozważyć następujące okno: Oto czarna linia jest linią bazową, podczas gdy odległość między czarną a czerwoną linią jest zejściem, odległość między czarną a karmazynową linią jest wzniesieniem, a odległość między karmazynową linią a niebieskim jest wiodąca. Kod komponentu jest pokazany poniżej. class Drawing extends JComponent public void paintcomponent(graphics g) String text = "Frode Fredegod"; Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Font font = new Font("Times New Roman", Font.PLAIN, 64); g2d.setfont(font); g2d.drawstring(text, 50, 100); g2d.draw(new Line2D.Double(25, 100, 500, 100)); FontMetrics metrics = g2d.getfontmetrics(); g2d.setpaint(color.blue); g2d.draw(new Line2D.Double(25, 100 metrics.getascent() - metrics.getleading(), 500, 100 metrics.getascent() - metrics.getleading())); g2d.setpaint(color.magenta); g2d.draw(new Line2D.Double(25, 100 metrics.getascent(), 500, metrics.getascent()));

55 g2d.setpaint(color.red); g2d.draw(new Line2D.Double(25, metrics.getdescent(), 500, metrics.getdescent())); int width = metrics.stringwidth(text); g2d.fill(new Rectangle2D.Double(width + 60, 90, 10, 10)); W tym miejscu powinieneś zwrócić uwagę na klasę FontMetrics, która zawiera informacje o czcionce, i powinieneś zwrócić uwagę na metodę stringwidth (), która służy do pomiaru, ile ciąg wypełnia bieżącą czcionkę. W rzeczywistości można więcej z tekstem i na przykład można powiązać iterator z tekstem, który może służyć do manipulowania poszczególnymi znakami lub częścią łańcucha. Program AttributeText otwiera okno, jak pokazano poniżej: class Drawing extends JComponent public void paintcomponent(graphics g) Font seriffont = new Font("Serif", Font.PLAIN, 48); Font monofont = new Font("Monospaced", Font.PLAIN, 48); String text = "Frode Fredegod, konge,,"; AttributedString str = new AttributedString(text); Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); str.addattribute(textattribute.font, seriffont); str.addattribute(textattribute.font, monofont, 6, 14); str.addattribute(textattribute.foreground, Color.red, 6, 14); str.addattribute(textattribute.underline, TextAttribute.UNDERLINE_ON, 6, 14); GeneralPath shape = new GeneralPath();

56 shape.moveto(0, -21); shape.lineto(10, -40); shape.lineto(20, -21); shape.closepath(); shape.moveto(0, -19); shape.lineto(10, 0); shape.lineto(20, -19); shape.closepath(); ShapeGraphicAttribute shapeattribute = new ShapeGraphicAttribute(shape, GraphicAttribute.ROMAN_BASELINE, ShapeGraphicAttribute.FILL); str.addattribute(textattribute.char_replacement, shapeattribute, 14, 15); str.addattribute(textattribute.foreground, Color.gray, 14, 15); Shape space = new Rectangle2D.Double(0, 0, 60, 0); ShapeGraphicAttribute spaceattribute = new ShapeGraphicAttribute(space, GraphicAttribute.ROMAN_BASELINE, ShapeGraphicAttribute.FILL); str.addattribute(textattribute.char_replacement, spaceattribute, text.length() 2, text.length() 1); Image image = createimage(); ImageGraphicAttribute imageattribute = new ImageGraphicAttribute(image, GraphicAttribute.TOP_ALIGNMENT); str.addattribute(textattribute.char_replacement, imageattribute, text.length() 1, text.length()); g2d.drawstring(str.getiterator(), 40, 80); public Image createimage() java.net.url imgurl = Drawing.class.getResource("/attributedtext/images/Bean.png"); ImageIcon icon = new ImageIcon(new ImageIcon(imgURL, ""). getimage().getscaledinstance(48, 48, Image.SCALE_SMOOTH), ""); return icon.getimage();

57 Metoda createimage () to metoda, która ładuje plik Bean.png z pliku jar, a wynikiem jest ikona o wymiarach pikseli. Następnie jest paintcomponent (), który tym razem jest złożony. Najpierw metoda definiuje dwie czcionki i ciąg znaków. Należy zauważyć, że ciąg znaków kończy się dwoma przecinkami. Nie jest konieczne, aby był to przecinek, ale te dwie znaki są następnie zastępowane przez coś innego. Kolejnym krokiem jest zdefiniowanie atrybutu dla tego łańcucha i jest to obiekt, który pozwala manipulować łańcuchem na wiele różnych sposobów. Najpierw dołączana jest czcionka szeryfowa dla tekstu, a następnie tekst zostanie na ogół narysowany na podstawie tej czcionki, ale następnie zostanie przypisana czcionka mono, ale tylko dla znaków od pozycji 6 i do pozycji 13 włącznie to słowo Fredegod. Również metoda określa, że ten podciąg powinien być czerwony i podkreślony. Dalej zdefiniowano Kształt jako GeneralPath, który jest postacią dwóch trójkątów. Ta figura jest reprezentowana jako ShapeGraphicAttribute, która wskazuje jej położenie w tekście (tutaj w linii podstawowej) i że powinna być narysowana jako wypełniona figura. Jest teraz wstawiany do tekstu na pozycji 14, aby zastąpić pierwszy przecinek (przecinek po słowie Fredegod). Kolejnym krokiem jest zdefiniowanie prostokąta i wstawienie go w ten sam sposób w przedostatnim miejscu. Prostokąt niczego nie wypełnia i powinien pokazywać tylko, jak dodać spację w tekście. Ostatecznie ikona jest wstawiana na ostatnim miejscu. Dzieje się tak samo, tylko tym razem wykorzystano inny obiekt, który ma typ ImageGraphicAttribute. Przykład pokazuje, jak zbudować bardzo złożony ciąg, i istnieje wiele innych opcji niż to, co pokazano powyżej. ĆWICZENIE 12 Tekst jest obiektem graficznym w taki sam sposób jak wszystkie inne kształty, a zatem tekst można przekształcić w taki sam sposób, jak inne obiekty Kształt. Napisz program, który otworzy następujące okno: ĆWICZENIE 13 Napisz program, który wyświetla okno, jak pokazano poniżej, w którym tekst jest rysowany za pomocą

58 TexturePaint i gdzie jest rysowany obrazami składającymi się z 4 małych kwadratów: 5.1 CZCIONKI Java2D zawiera kilka klas dotyczących czcionek, a ja już wspomniałem o klasach Font i FontMetrics. W tej sekcji pokażę program, który otwiera okno, jak pokazano poniżej. Aplikacja wyświetla pole listy z listą wszystkich dostępnych czcionek. Dwukrotne kliknięcie linii powoduje wyświetlenie okna komunikatu z informacją o czcionce. Kod jest następujący i nie ma wiele do wyjaśnienia, ale należy szczególnie zwrócić uwagę, jak określić, które czcionki są dostępne. Zwróć również uwagę na klasę FontRenderContext, która może być przydatna do pomiaru ilości napełnionego łańcucha podczas rysowania określoną czcionką: package fontprogram; import java.awt.*; import java.awt.font.*;

59 import java.awt.event.*; import java.awt.geom.*; import javax.swing.*; public class MainWindow extends JFrame private DefaultListModel model = new DefaultListModel(); public MainWindow() super("fontprogram"); setsize(700, 400); setlocationrelativeto(null); createwindow(); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createwindow() Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); for (Font font : fonts) model.addelement(font); JList list = new JList(model); add(new JScrollPane(list)); list.addmouselistener(new MouseHandler()); class MouseHandler extends MouseAdapter public void mouseclicked(mouseevent e)

60 if (e.getclickcount() == 2) try JList list = (JList)e.getSource(); int n = list.locationtoindex(e.getpoint()); Font font = (Font)model.get(n); StringBuilder builder = new StringBuilder(font.getFamily()); builder.append("\n"); builder.append(font.getname()); Graphics2D g2d = (Graphics2D)list.getGraphics(); g2d.setfont(new Font(font.getFamily(), font.getstyle(), 24)); FontRenderContext frc = g2d.getfontrendercontext(); Rectangle2D rect1 = g2d.getfont().getstringbounds("abc", frc); Rectangle2D rect2 = g2d.getfont().getstringbounds("knud den Hellige", frc); builder.append("\n"); builder.append(rect1); builder.append("\n"); builder.append(rect2); JOptionPane.showMessageDialog(MainWindow.this, builder.tostring(), font.getfontname(), JOptionPane.INFORMATION_MESSAGE); catch (Exception ex) ĆWICZENIE 14 Napisz program, który otworzy poniższe okno kiedy

61 1. długość boku wypełnionego kwadratu jest szerokością dużej litery A w bieżącej czcionce 2. linia pokrywa się z linią bazową czcionki 3. kwadraty jest na linii podstawowej dla czcionki 4. prostokąty są prostokątami opisującymi tekst 5. odległość między prostokątem a kwadratem jest szerokością dużego A Ponieważ stosowane są rozmiary czcionek 12, 24, 36 i 72 punkty. 5.2 UKŁAD TEKSTOWY Ten rozdział o tekście zakończę klasą TextLayout, która pozwala całkowicie kontrolować sposób renderowania tekstu. W praktyce program może wyświetlać tekst za pomocą JTextField, JTextArea lub JEditorPane, ale jeśli masz bardzo specyficzne potrzeby, gdzie te komponenty nie są odpowiednie, zapewniając dodatkową elastyczność TextLayout. Poniżej z czterema przykładami przedstawię niektóre opcje. TextLayout jest używany jako alternatywa dla drawstring (), a obiekt klasy można traktować jako enkapsulację łańcucha z rozszerzonymi możliwościami manipulowania nim. Program TextProgram otwiera następujące okno: Oto pierwszy tekst (czarny tekst) narysowany za pomocą TextLayout, podczas gdy drugi to Kształt utworzony z czarnego tekstu, a zatem kształt, który składa się ze wszystkich glifów tekstu. Jeśli klikniesz gdzieś w górnym tekście (na przykład dużą literę H), pojawi się okno komunikatu, jak pokazano poniżej, które pokazuje kliknięty znak:

62 Kody komponentu są następujące: class Drawing extends JComponent private String str = "Knud den Hellige"; private Font font = new Font("Liberation Serif", Font.BOLD, 36); private TextLayout text; private int xpos = 20; private int ypos = 50; public Drawing() FontRenderContext context = new FontRenderContext(new AffineTransform(), false, false); text = new TextLayout(str, font, context); addmouselistener(new HitTester()); public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); text.draw(g2d, xpos, ypos); Shape shape = text.getoutline(affi 100)); g2d.setpaint(color.yellow); g2d.fill(shape); g2d.setpaint(color.red); g2d.draw(shape); class HitTester extends MouseAdapter public void mouseclicked(mouseevent e) Rectangle2D rect = text.getbounds(); if (rect.contains(e.getx() xpos, e.gety() ypos))

63 TextHitInfo hit = text.hittestchar(e.getx() xpos, e.gety() ypos); JOptionPane.showMessageDialog( Drawing.this, str.charat(hit.getcharindex())); Klasa definiuje zmienną instancji typu tekst TextLayout. Jest tworzony w konstruktorze jako enkapsulacja ciągu str. Konstruktor obiektu TextLayout wymaga Font i FontRenderContext. Należy pamiętać, jak utworzyć FontRenderContext. Pierwszy parametr to AffineTransform, który tutaj jest tylko tożsamością, podczas gdy ostatni określa, czy użyć antyaliasingu i jak obliczać rozmiary. Na koniec przypisz konstruktorowi detektor kliknięć myszą, co wyjaśnię za chwilę. Jeśli masz TextLayout, możesz narysować tekst w następujący sposób: text.draw (g2d, xpos, ypos) Składnia jest nieco inna niż w przypadku rysowania kształtu lub tekstu. Po narysowaniu rysunku możesz za pomocą metody getoutline () uzyskać kształt składający się z glifów tekstu. Metoda ma transformację, która mówi, w jaki sposób obiekty są transformowane, a obiekt Shape można następnie traktować w taki sam sposób, jak inne obiekty Shape. W takim przypadku rysunek jest wypełniony żółtym kolorem i narysowany czerwonym. Składnik jest określony w konstruktorze, który nasłuchuje kliknięć myszy, a moduł obsługi sprawdza, czy współrzędne myszy mieszczą się w obiekcie TextLayout. Chodzi o to, że sprawa jest przywołana w obiekcie TextHitInfo obiektu, który zwraca pewne ważne informacje, przy czym najważniejsze jest przekonwertowanie współrzędnych myszy na kliknięty znak. Ustalenie go nie jest trywialne i jest to jedna z usług oferowanych przez TextLayout. Następny przykład otworzy następujące okno, które ponownie pokazuje tekst narysowany za pomocą TextLayout: Program pokaże, jak narysować kursor (lub kursor / marker), a także jak przesuwać kursor za pomocą klawiszy strzałek. Brzmi prosto, ale jeśli się nad tym zastanowić, kursor jest (zwykle) cienką linią rysowaną między dwiema literami i nie jest łatwo obliczyć, jak należy narysować ten ciąg. Na szczęście klasa TextLayout udostępnia niezbędne elementy. Kod komponentu pokazano poniżej: class Drawing extends JComponent

64 private String str = "Knud den Hellige"; private Font font = new Font("Liberation Serif", Font.BOLD, 36); private TextLayout text; private int xpos = 20; private int ypos = 50; private TextHitInfo hit; public Drawing(MainWindow main) FontRenderContext context = new FontRenderContext(new AffineTransform(), false, false); text = new TextLayout(str, font, context); hit = text.getnextlefthit(1); main.addkeylistener(new KeyHandler()); public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setrenderinghint(renderinghints.key_fractionalmetrics, RenderingHints.VALUE_FRACTIONALMETRICS_ON); text.draw(g2d, xpos, ypos); Shape[] carets = text.getcaretshapes(hit.getinsertionindex()); if (carets[0]!= null) Shape shape = AffineTransform.getTranslateInstance(xpos, ypos).createtransformedshape(carets[0]); g2d.setstroke(new BasicStroke()); g2d.draw(shape); class KeyHandler extends KeyAdapter

65 public void keypressed(keyevent e) if (e.getkeycode() == KeyEvent.VK_RIGHT) hit = text.getnextrighthit(hit.getinsertionindex()); if (hit == null) hit = text.getnextlefthit(1); repaint(); else if (e.getkeycode() == KeyEvent.VK_LEFT) hit = text.getnextlefthit(hit.getinsertionindex()); if (hit == null) hit = text.getnextrighthit(text.getcharactercount() 1); repaint(); Kod jest podobny do poprzedniego przykładu, ale konstruktor ma tym razem odwołanie do głównego okna, a następnie do powiązania procedury obsługi klawiatury z oknem. Moduł obsługi zdarzeń jest definiowany przez klasę KeyHandler, która jest klasą wewnętrzną w klasie Drawing. Klasa ma tym razem zmienną instancji typu TextHitInfo. Pokaże, gdzie należy narysować kursor, i zostanie zainicjowany w konstruktorze, a następnie następne trafienie zostanie narysowane po lewej stronie znaku o indeksie 1 - a zatem pierwszy znak. Następnie jest paintcomponent (), który tym razem określa dodatkową wskazówkę dotyczącą renderowania. Wskazuje, jak powinno się określać granice postaci i z jaką dokładnością. Po narysowaniu tekstu określa się pozycję kursora: Shape [] carets = text.getcaretshapes (hit.getinsertionindex ()); TextLayout nie rodzi się z daszkiem, ale ma logiczną pozycję, która jest dołączona do obiektu trafienia i którą można odnieść do metody getinsertionindex (). Metoda getcaretshapes () zwraca kształt do karetki. W rzeczywistości zwraca dwa, a powodem jest to, że Java2D obsługuje tekst dwukierunkowy, a zatem tam, gdzie tekst jest pisany od prawej do lewej. Dlatego zwraca powyższą metodę dwa obiekty Shape. Mając dostępny obiekt Shape do karetki, po transformacji należy go narysować jak każdy inny plik. Tutaj musisz sprecyzować metodę createtransformedshape (), która przekształca plik bez przekształcania bieżącego obiektu Graphics2D. Następnie jest moduł obsługi zdarzeń dla klawiatury, który traktuje zdarzenia dotyczące lewej i prawej strzałki. Gdy patrzysz na moduł obsługi, łatwo go zrozumieć i działa przy użyciu dwóch metod getnextrighthit () i getnextlefthit (). Następny przykład jest podobny do powyższego i otwiera następujące okno:

66 Program pokaże na przykład, jak zaznaczyć tekst za pomocą myszy. Po zaznaczeniu tekstu i zwolnieniu myszy pojawi się okno komunikatu z zaznaczonym tekstem. class Drawing extends JComponent private String str = "Knud den Hellige, Danisk king"; private Font font = new Font("Liberation Serif", Font.BOLD, 36); private TextLayout text; private int xpos = 20; private int ypos = 50; private TextHitInfo hit1; private TextHitInfo hit2; private Rectangle2D rect; public Drawing() FontRenderContext context = new FontRenderContext(new AffineTransform(), false, false); text = new TextLayout(str, font, context); rect = text.getbounds(); addmouselistener(new HitTester()); addmousemotionlistener(new MoveTester()); public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint(

67 RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setrenderinghint(renderinghints.key_fractionalmetrics, RenderingHints.VALUE_FRACTIONALMETRICS_ON); if (hit1!= null && hit2!= null) Shape base = text.getlogicalhighlightshape(hit1.getinsertionindex(), hit2.getinsertionindex()); Shape rect = AffineTransform. gettranslateinstance(xpos, ypos).createtransformedshape(base); g2d.setpaint(color.orange); g2d.fill(rect); g2d.setpaint(color.black); text.draw(g2d, xpos, ypos); class HitTester extends MouseAdapter public void mousepressed(mouseevent e) hit1 = text.hittestchar(e.getx() xpos, e.gety() ypos); hit2 = null; repaint(); public void mousereleased(mouseevent e) if (hit1!= null && hit2!= null) int n1 = hit1.getinsertionindex(); int n2 = hit2.getinsertionindex(); if (n1 > n2) int n = n2; n2 = n1; n1 = n;

68 JOptionPane.showMessageDialog(Drawing.this, str.substring(n1, n2)); hit2 = null; class MoveTester extends MouseMotionAdapter public void mousedragged(mouseevent e) if (rect.contains(e.getx() xpos, e.gety() ypos)) hit2 = text.hittestchar(e.getx() xpos, e.gety() ypos); repaint(); Kod jest w zasadzie dość prosty i polega przede wszystkim na użyciu dwóch obiektów TextHitInfo w celu ustalenia, która część tekstu jest zaznaczona i wypełnienia odpowiedniego prostokąta. Najtrudniejszą rzeczą jest kontrolowanie logiki w procedurach obsługi zdarzeń pod względem, kiedy rozpoczyna się nowa selekcja i kiedy znacznik wyboru musi znowu zniknąć. TextLayout reprezentuje w zasadzie pojedynczy wiersz tekstu, a jeśli tekst musi wypełnić kilka wierszy, musisz zrobić lepiej, ale na szczęście istnieją klasy pomocnicze, których można użyć. Rozważ następujące okno. Jedyne, co robi program, to wyświetlać tekst w oknie (i oczywiście byłoby to proste z JTextArea), ale w tym przypadku tekst jest rysowany za pomocą TextLayout

69 package multilineprogram; import java.awt.*; import java.awt.font.*; import javax.swing.*; import java.text.*; public class MainWindow extends JFrame public MainWindow() super("multilineprogram"); setsize(600, 400); setlocationrelativeto(null); add(new JScrollPane(new Drawing())); setdefaultcloseoperation(exit_on_close); setvisible(true); class Drawing extends JComponent

70 private String[] str = "Foreword", "This book is the tenth in a series of books on software development. The " + " programming language is Java, and the language and its syntax and " + "semantics fills obviously much, but the books have also largely " + "focus on the process and how to develop good and robust applications. " + "In the previous book, I have relatively detailed treated Swing, and " + "the subject of this book is Java2D, which is the other half of what " + "Java is making available for developing applications with a graphical " + "user interface. One can also think of Java2D as the graphical tools that " + "Swing uses to draw the components in a window. The book is relatively " + "detailed and addresses issues that are not used so often in " + "everyday programming, but the examples are, of course, and also the issues " + "are important to understand how the GUI works.", "It is similar to a few other books in this series a book where the focus " + "is on language Java over the process, and only the final example " + "focuses on system development with the development of Java class library.", "The book assumes knowledge of Java corresponding to the books Java 3 and " + "Java 4 and to some extent knowledge of Swing corresponding to the book " + "Java 2." ; private Font font1 = new Font("Verdana", Font.BOLD, 24); private Font font2 = new Font("Times New Roman", Font.PLAIN, 14); private Font font3 = new Font("Times New Roman", Font.PLAIN, 18); public void paintcomponent(graphics g) float height = 0; Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint(renderinghints.key_antialiasing, RenderingHints.VALUE_ANTIALIAS_ON); height = draw(g2d, new Insets(5, 5, 20, 5), 0, new AttributedString(str[0]), font1); height = draw(g2d, new Insets(0, 5, 10, 5), height, new AttributedString(str[1]), font2); height = draw(g2d, new Insets(0, 5, 10, 5), height, new AttributedString(str[2]), font2); height = draw(g2d, new Insets(0, 5, 5, 5), height, new AttributedString(str[3]), font3); setpreferredsize(new Dimension(0, (int)height)); private fl draw(graphics2d g, Insets margin, fl y, AttributedString str, Font font) str.addattribute(textattribute.font, font); AttributedCharacterIterator itr = str.getiterator();

71 FontRenderContext context = g.getfontrendercontext(); LineBreakMeasurer lbm = new LineBreakMeasurer(itr, context); float width = getsize().width margin.left margin.right; float x = margin.left; y += margin.top; while (lbm.getposition() < itr.getendindex()) TextLayout textlayout = lbm.nextlayout(width); y += textlayout.getascent(); textlayout.draw(g, x, y); y += textlayout.getdescent() + textlayout.getleading(); x = margin.left; y += margin.bottom; return y; Tekst do narysowania jest zdefiniowany jako trzy ciągi w tablicy i każdy ciąg może traktować jako akapit. Najważniejszą rzeczą jest metoda draw () z rysowanym obiektem graficznym. Metoda ma jako parametr akapit w postaci atrybutu ciąg, czcionkę, za pomocą której ma zostać narysowany tekst, pozycję y miejsca, w którym ma zostać narysowany, oraz obiekt wstawki, który wskazuje, ile powietrza powinno być na zewnątrz tekst. Podstawowym zadaniem metody jest podzielenie tekstu na linie i do tego celu używana jest klasa LineBreakMeasure. Oprócz tego metoda powinna kontrolować pozycję y dla każdej linii. Komponent znajduje się w konstruktorze głównego okna umieszczonym w JScrollPane. W związku z tym, paintcomponent () musi zdefiniować rozmiar komponentu. Z zasady tego przykładu można skorzystać, jeśli chcesz napisać niestandardowy komponent, który obsługuje zawijanie tekstu. 5.3 GLYFY W poprzedniej sekcji zasugerowałem możliwości TextLayout i chociaż rzadko potrzebujesz możliwości klasy, istnieją przykłady, na przykład, jeśli chcesz opracować własne komponenty. Korzystanie z TextLayout jest stosunkowo skomplikowane, ale w rzeczywistości można zejść jeszcze niżej i bezpośrednio manipulować poszczególnymi glifami. Rzadko - jeśli w ogóle - będziesz go potrzebować, ale poniższy program powinien pokazać, że jest to możliwe. Program otwiera okno pokazane na następnej stronie. Jeśli masz ciąg znaków, klasa Font ma metodę, która zwraca tak zwany GlyphVector, który jest kolekcją składającą się z obiektów Glyph zdefiniowanych przez ten ciąg. Obiektami tymi, z których każdy jest obiektem Shape, można następnie manipulować w taki sam sposób, jak każdy inny kształt. class Drawing extends JComponent public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g;

72 g2d.setrenderinghint(renderinghints.key_antialiasing, RenderingHints.VALUE_ANTIALIAS_ON); String str = "Knud den Hellige"; Font font = new Font("Serif", Font.PLAIN, 36); FontRenderContext context = g2d.getfontrendercontext(); g2d.translate(20, 50); GlyphVector glyps = font.createglyphvector(context, str); int length = glyps.getnumglyphs(); for (int i = 0; i < length; ++i) Point2D p = glyps.getglyphposition(i); AffineTransform trans = AffineTransform.getTranslateInstance(p.getX(), p.gety()); trans.rotate(i / (double)(length 1) * Math.PI / 4); g2d.fill(trans.createtransformedshape(glyps.getglyphoutline(i))); 6 KOLORY Kolory są traktowane w kilku miejscach w tej serii książek, a jako programista zwykle nie musisz wiedzieć więcej niż to, co zostało już powiedziane. Kolory powstają poprzez połączenie wartości trzech kolorów: czerwonego, zielonego i niebieskiego, dlatego mówimy o kodowaniu kolorów RGB. Ponadto powiązana jest wartość alfa, która określa stopień przezroczystości koloru. Każda z czterech wartości

73 jest reprezentowana przez bajt (a zatem może mieć 256 różnych wartości), a wewnętrznie kolor jest reprezentowany przez 32-bitową liczbę int: Podstawową klasą reprezentującą kolor jest Kolor i był on używany wiele razy wcześniej. Patrząc na wszystkie poprzednie przykłady, użycie kolorów jest na ogół proste, ale w rzeczywistości kolory takie same jak tekst są niezwykle złożone, dlatego też ten rozdział stanowi krótkie wprowadzenie do problemów dotyczących kolorów. Krótko mówiąc, problemy można wyjaśnić faktem, że niektóre kolory mogą wyglądać inaczej (i często tak robią) na różnych ekranach, a nawet gorzej, jeśli są drukowane na kolorowej drukarce. Powodem jest to, że komputerowa reprezentacja kolorów jest względna (co to właściwie oznacza, że kolor jest 40% czerwony?) I że różne urządzenia sprzętowe (ekran i drukarka) nie wyświetlają tego samego koloru w ten sam sposób. Wyjaśnię trochę poniżej, ale najpierw przykład. Aplikacja ColorProgram otwiera następujące okno: pokazano 39 kolorowych prostokątów. Górny rząd pokazuje wartości kolorów, które są zdefiniowane jako stałe w klasie Kolor, a nad oknem pokazuje zielony i niebieski prostokąt, a środkowy rząd prostokątów pokazuje, jak można mieszać te dwa kolory, tak aby prostokąt po lewej stronie miał zielony i gdy przesuniesz się w prawo, kolor zielony jest mieszany z kolorem niebieskim, a na końcu ostatni jest niebieski. Dolny rząd prostokątów pokazuje (od prawej) kolor niebieski coraz bardziej przezroczysty do lewej, aby był całkowicie przezroczysty, a zatem niewidoczny. U góry znajdują się dwa przyciski, które służą do otwarcia standardowego okna dialogowego wyboru kolorów (patrz poniżej), dzięki czemu można zmienić dwa kolory, którymi manipuluje program - to znaczy kolory dwóch prostokątów u góry okno. W ten sposób możesz eksperymentować i zobaczyć, co się stanie, gdy zmieszane kolory i jak zmienia się wartość alfa koloru. Okno dialogowe jest stosunkowo złożone z wieloma opcjami, ale można utworzyć obiekt ColorChooser, który jest składnikiem, który można skonfigurować tak, aby wyświetlał pożądane opcje.

74 Cały kod pokazano również poniżej i nie zawiera on tak wielu nowych: package colorprogram; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import javax.swing.*; public class MainWindow extends JFrame private Color leftcolor = Color.green; private Color rightcolor = Color.blue; private JLabel lblleft = new JLabel(); private JLabel lblright = new JLabel(); private Drawing drawing; public MainWindow() super("colorprogram"); setsize(700, 250); setlocationrelativeto(null); add(createtop(), BorderLayout.NORTH); add(drawing = new Drawing());

75 setdefaultcloseoperation(exit_on_close); setvisible(true); private JPanel createtop() JPanel panel = new JPanel(new GridLayout(1, 2)); JPanel left = new JPanel(new BorderLayout()); JPanel right = new JPanel(new BorderLayout()); JButton cmdleft = new JButton("Color"); JButton cmdright = new JButton("Color"); cmdleft.addactionlistener(this::updateleft); cmdright.addactionlistener(this::updateright); lblleft.setopaque(true); lblleft.setbackground(leftcolor); lblright.setopaque(true); lblright.setbackground(rightcolor); left.add(cmdleft, BorderLayout.WEST); left.add(lblleft); right.add(cmdright, BorderLayout.EAST); right.add(lblright); panel.add(left); panel.add(right); return panel; class Drawing extends JComponent private Color[] colors = Color.white, Color.lightGray, Color.gray, Color.darkGray, Color.black, Color.red, Color.pink, Color.orange, Color.yellow, Color.green, Color.magenta, Color.cyan, Color.blue ; public void paintcomponent(graphics gr)

76 Dimension d = getsize(); Graphics2D g2d = (Graphics2D)gr; g2d.setrenderinghint(renderinghints.key_antialiasing, RenderingHints.VALUE_ANTIALIAS_ON); float t = colors.length; float w = d.width / t; float h = d.height / 3.0F; for (int i = 0; i < t; ++i) g2d.setpaint(colors[i]); g2d.fill(new Rectangle2D.Float(i * w, 0, w, h)); for (int i = 0; i < t; ++i) float u = i / t; int r = (int)(rightcolor.getred() * u + leftcolor.getred() * (1 u)); int g = (int)(rightcolor.getgreen() * u + leftcolor.getgreen() * (1 u)); int b = (int)(rightcolor.getblue() * u + leftcolor.getblue() * (1 u)); g2d.setpaint(new Color(r, g, b)); g2d.fill(new Rectangle2D.Float(i * w, h, w, h)); for (int i = 0; i < t; ++i) int alpha = (int)(255 * i / t); g2d.setpaint(new Color(rightColor.getRed(), rightcolor.getgreen(), rightcolor.getblue(), alpha)); g2d.fill(new Rectangle2D.Float(i * w, 2 * h, w, h)); public void updateleft(actionevent e) Color color = JColorChooser.showDialog(this, "Select the left", leftcolor); if (color!= null) leftcolor = color; lblleft.setbackground(color); drawing.repaint();

77 public void updateright(actionevent e) Color color = JColorChooser.showDialog(this, "Select the right color", rightcolor); if (color!= null) rightcolor = color; lblright.setbackground(color); drawing.repaint(); Należy jednak szczególnie zwrócić uwagę na sposób mieszania dwóch kolorów i tworzenia obiektów kolorowych. Należy również zwrócić uwagę na sposób tworzenia obiektu JColorChooser, otwierając w ten sposób okno dialogowe wyboru koloru, i należy zapoznać się z dokumentacją, w których dostępnych opcjach można dostosować to okno dialogowe. Możesz wyłączyć wiele opcji, a okno dialogowe powinno być łatwiejsze w zarządzaniu. 6.1 O KOLORACH W świecie fizyki kolory są światłem reprezentowanym przez fale elektromagnetyczne, a kolor światła zależy od długości fali światła. Ludzkie oko używa specjalnych komórek zdolnych do przechwytywania tych długości fal i przekształcania tych wrażeń do naszego mózgu, w rzeczywistości poprzez przechwytywanie trzech intensywności kolorów: czerwonego, zielonego i niebieskiego, a więc trochę takich samych, jak komputer pracuje z kolorami RGB. Jednak nasze oczy są znacznie lepsze od komputera. Ekran musi przekonwertować trzy wartości kolorów na określony kolor, co odbywa się za pomocą specjalnych funkcji, ale te funkcje nie mogą tworzyć wszystkich kolorów, więc kolory na ekranie komputera lub drukarki będą zawsze zbliżone do rzeczywistych kolorów. Pokój kolorów to rodzina kolorów, które mogą być wyświetlane na określonym urządzeniu. Na przykład ma ekran z pewną rodziną kolorów, którą może wyświetlić. Kolory te powstają z intencji kolorów czerwonego, zielonego i niebieskiego, aw praktyce istnieje 256 wartości dla każdego koloru. Dwa różne ekrany nie mają tej samej przestrzeni kolorów ze względu na odmiany czerwonego, zielonego i niebieskiego światła oraz odmiany elektroniki producenta. Chociaż dwa ekrany używają tego samego kodowania kolorów w postaci RGB, kolory nadal będą się różnić, a każdy z ekranów będzie miał swój pokój kolorów. Wszystko to komplikują niektóre drukarki wykorzystujące kodowanie w innym kolorze, łączące kolory: cyjan, magenta, żółty i czarny. To kodowanie nazywa się CMYK, a taka drukarka ma zamiast tego pokój kolorów CMYK. W związku z tym kolory RGB muszą zostać przekonwertowane na kolory CMYK, aby były wyświetlane na drukarce, co mówi inaczej, że pomieszczenie kolorów dla bieżącego wyświetlacza powinno zostać przekonwertowane na pomieszczenie kolorów innego

78 urządzenia. Aby rozwiązać te problemy, przemysł pracował nad zdefiniowaniem pokojów w kolorze absolutnym, które precyzyjnie określają, co jest czerwone, co zielone, a co niebieskie. Jedna z nich nazywa się srgb (dla standardowego RGB) i jest domyślnie używana przez Java 2D, która konwertuje kolory do tego pokoju kolorów. Pomaga rozwiązać problemy z kolorami, ale nie kompensuje różnic w sprzęcie i używanych profilach kolorów. Jest to tabela, która konwertuje kolory ze standardowego pokoju kolorów, takiego jak srgb, do pokoju kolorów dla określonego urządzenia fizycznego. Każde urządzenie fizyczne ma więc swój własny profil kolorów. Dotyczy to na przykład ekranu i aby kolory wyglądały poprawnie, możesz skalibrować ekran, co oznacza dostosowanie profilu kolorów, aby konwersja z określonej przestrzeni kolorów na profil była prawidłowa. Nie jest to łatwe i wymaga praktyki i wiedzy, ale ludzie, którzy pracują z drukiem, robią wiele, aby właściwie skalibrować swój sprzęt. Dla wielu aplikacji (większość) wszystko z pokojami kolorów i profilami kolorów jest nieciekawe. Jeśli program musi narysować figurę, rzadko ważne jest, aby zobaczyć, czy figura pojawia się w innym kolorze na innym ekranie, ale dzieje się tak na przykład, jeśli musisz pokazać zdjęcia, gdzie jest to bardzo ważne. Tutaj kolory powinny być odpowiednie, a jeśli piszesz oprogramowanie do obrazowania, powyższe jest ważne. Chociaż nie chcę dalej wchodzić w ten temat, typy Java 2D są dostępne zarówno dla przestrzeni kolorów, jak i profili kolorów, typów, które mają metody manipulowania szczegółami dla kolorów. Aby zakończyć tę sekcję dotyczącą kolorów, pokażę program, który wypełnia elipsę farbą gradientową, ale w przeciwieństwie do tego, co pokazałem wcześniej, musi to być farba gradientowa, która łączy dwa kolory od środka do obrzeża elipsy. Program otwiera okno, jak pokazano poniżej: W taki sam sposób, jak pokazano w poprzednim przykładzie (Ćwiczenie 8), możesz zmienić dwa kolory za pomocą komponentów JSlider w górnej i dolnej części okna. Java 2D nie ma bezpośrednio do tego celu obiektu Paint i dlatego konieczne jest napisanie samej klasy. Klasa powinna implementować interfejs Paint, który definiuje dwie metody. Najważniejszy nazywa się createcontext () i zwraca obiekt PaintContext, który jest także interfejsem, w którym najważniejsza metoda nazywa się getraster (), i zwraca obiekt Raster do rysowanej figury. Jest to obiekt ze wszystkimi pikselami zmodyfikowanymi. Kod obiektu Paint jest pokazany poniżej i nie chcę dalej opisywać kodu, ponieważ najważniejsze wyjaśniono w komentarzach: package gradientprogram;

79 import java.awt.*; import java.awt.geom.*; import java.awt.image.*; // Defi a Paint object, which is a gradient paint that blends two colors from // a center point and along a radius. The color at the center is color1, whereas // all points whose distance to the center is greater than or equal to the radius // is color2. All points, whose distance to the center is less than the radius // has a gradient color, so that all points with the same distance to the center // have the same color. public class RoundGradient implements Paint private Point2D point; // the center private Point2D radius; // the radius private Color color1; // the color at the center // color of points whose distance to center is greater than radius private Color color2; public RoundGradient(Point2D point, Color color1, Point2D radius, Color color2) this.point = point; this.color1 = color1; this.radius = radius; this.color2 = color2; // This method is defined by the interface Paint. public PaintContext createcontext(colormodel cm, Rectangle devicebounds, Rectangle2D userbounds, AffineTransform trans, RenderingHints hints)

80 return new RoundContext(trans.transform(point, null), color1, trans.deltatransform(radius, null), color2); // This method is defined by the interface Paint. public int gettransparency() int a1 = color1.getalpha(); int a2 = color2.getalpha(); return (((a1 & a2) == 0xff)? OPAQUE : TRANSLUCENT); // PaintContext, that defines how the individual points should be colored class RoundContext implements PaintContext private Point2D point; // the center private Point2D radius; // the radius private Color color1; // the color at the center // color of points whose distance to center is greater than radius private Color color2; public RoundContext(Point2D point, Color color1, Point2D radius, Color color2) this.point = point; this.color1 = color1; this.radius = radius; this.color2 = color2; // This method is defined by the interface PaintContext. public void dispose()

81 // This method is defined by the interface PaintContext. public ColorModel getcolormodel() return ColorModel.getRGBdefault(); // This method is defined by the interface PaintContext. // The method's parameters define the area to be colored. public Raster getraster(int x, int y, int w, int h) // defines a Raster with w*h pixels WritableRaster raster = getcolormodel().createcompatiblewritableraster(w, h); // array to color values, // there are used 4 places to each pixel (red, green, blue, alpha) int[] data = new int[w * h * 4]; double rad = radius.distance(0, 0); // loop over all pixels for (int j = 0; j < h; ++j) for (int i = 0; i < w; i++) // the distance between the center and the current point measured to raidus // must max be 1 double r = point.distance(x + i, y + j) / rad; if (r > 1.0) r = 1.0; // pixel index int b = (j * w + i) * 4; // define pixel values // if r is close to 0, color1 is used // If r is close to 1, color2 is used // else the two colors are mixed data[b] = (int)(color1.getred() + r * (color2.getred() color1.getred()));

82 data[b + 1] = (int)(color1.getgreen() + r * (color2.getgreen() color1.getgreen())); data[b + 2] = (int)(color1.getblue() + r * (color2.getblue() color1.getblue())); data[b + 3] = (int)(color1.getalpha() + r * (color2.getalpha() color1.getalpha())); // copy the pixel values to the Raster raster.setpixels(0, 0, w, h, data); return raster; Należy zauważyć, że klasa jest stosunkowo ogólna i może być używana do malowania obiektów w innych kontekstach. Zauważ, że punkt środkowy nie musi być środkiem. Pamiętaj również, że możesz użyć tego samego koloru dla obu wartości, ale z różnymi wartościami alfa. Poniżej znajduje się komponent, który rysuje rysunek: class Drawing extends JComponent public void paintcomponent(graphics g) Dimension d = getsize(); Color color1 = new Color(sldFgR.getValue(), sldfgg.getvalue(), sldfgb.getvalue()); Color color2 = new Color(sldBgR.getValue(), sldbgg.getvalue(), sldbgb.getvalue()); Graphics2D g2d = (Graphics2D)g; g2d.setrenderinghint(renderinghints.key_antialiasing, RenderingHints.VALUE_ANTIALIAS_ON); Ellipse2D ellip = new Ellipse2D.Double(20, 20, d.width 40, d.height 40); Point2D rad = d.width < d.height? new Point2D.Double(20, d.height / 2) : new Point2D.Double(d.width / 2, 20); g2d.setpaint(new RoundGradient(new Point2D.Double(d.width / 2, d.height / 2), color1, rad, color2)); g2d.fill(ellip); PROBLEM 1 Musisz napisać program, który tworzy okrągły przycisk. Na przykład poniżej pokazano okno z czterema przyciskami:

83 Przyciski powinny być zdefiniowane jako komponenty niestandardowe i muszą obsługiwać zawijanie tekstu. Kliknięcie przycisku musi również wywołać efekt, a na koniec komponent w naturalny sposób uruchomi zdarzenie akcji po kliknięciu go. 7 OBRAZY Obraz jest dwuwymiarową tablicą kolorów, a każdy element nazywany jest pikselem. Zasadniczo wygląda jak każda inna postać, ale istnieją dwie ważne różnice 1. piksele niekoniecznie reagują bezpośrednio na piksele na rysunku 2. obraz ma szerokość i wysokość mierzoną w pikselach oraz układ współrzędnych niezależny od obszaru rysowania Podstawową klasą jest Obraz, ale są też inne. Pierwszym krokiem jest pokazanie, jak załadować obraz, aby można go wyświetlić w oknie. Właściwie już pokazałem, jak, na przykład, ładując ikonę lub zdjęcia do kart do gry itp. Na przykład poniższa metoda odczytuje obraz z pliku, w którym parametrem jest nazwa pliku (pełna nazwa ścieżki): public static ImageIcon loadimageicon(string filename) return new ImageIcon(filename); Obraz jest reprezentowany jako ImageIcon, a jeśli chcesz odwoływać się do obrazu jako Image, klasa ImageIcon ma metodę o nazwie getimage (). Łatwiej nie może być. Następująca metoda robi to samo: public static BufferedImage loadimage(string filename) try return ImageIO.read(new File(filename)); catch (IOException e)

84 return null; a główna różnica polega na tym, że metoda tym razem zwraca obraz jako buforowany obraz, dzięki czemu można bezpośrednio manipulować obrazem. Należy w szczególności zauważyć, że BufferedImage dziedziczy klasę Image i dlatego może być używany wszędzie tam, gdzie można użyć Image. Zamiast tego, jeśli chcesz odczytać obraz z pliku jar programu (co często ma miejsce w przypadku ikon i mniejszych obrazów), możesz użyć następującej metody: public static ImageIcon createimageicon(string path) java.net.url imgurl = PaGUI.class.getResource(path); if (imgurl!= null) return new ImageIcon(imgURL, ""); return null; gdzie parametrem powinna być nazwa obrazu w stosunku do pakietu zawierającego obraz. Odmianą tej metody, która skaluje obraz do określonego rozmiaru (i jest szczególnie przydatna podczas ładowania ikon) jest: public static ImageIcon createimageicon(string path, int width, int height) java.net.url imgurl = PaGUI.class.getResource(path); if (imgurl!= null) return new ImageIcon(new ImageIcon(imgURL, ""). getimage().getscaledinstance(width, height, Image.SCALE_SMOOTH), ""); return null; Wyświetlanie obrazu w oknie jest w zasadzie proste, a następująca klasa pokazuje obraz: class Drawing extends JComponent public void paintcomponent(graphics g) if (image!= null) Graphics2D g2d = (Graphics2D)g; AffineTransform trans = new AffineTransform(); g2d.drawimage(image, trans, this);

85 else super.paintcomponent(g); i nie ma wiele do wyjaśnienia. Graphics2D ma metodę drawimage (), która pokazuje obraz. Obraz obiektu to obraz, a trans to transformacja, która przekształca obraz przed jego wyświetleniem. W tym przypadku nie ma znaczenia tylko tożsamość, w wyniku czego obraz wydaje się nieskalowany. Oznacza to, że jeśli obraz jest większy niż komponent, pojawi się tylko niewielka część obrazu, a wynik może być taki, jak pokazano poniżej (po kliknięciu lewego przycisku). Jest to duży obraz (3306 x 1860 pikseli) i widać tylko lewy górny róg obrazu. Możesz użyć parametru trans do skalowania obrazu, a ja chcę pokazać trzy sposoby. Podobnie program definiuje następujący typ: package imageprogram; public enum ImageScaling UNSCALED, STRETCHED, SCALEDTOFIT, SCALEDTOFILL jak wskazuje 1. obraz powinien być nieskalowany 2. obraz powinien być skalowany w obu kierunkach, aby dokładnie wypełniał okno 3. obraz powinien być skalowany w obu kierunkach, ale w taki sposób, aby zachować proporcje i wyświetlić cały obraz 4. obraz powinien być skalowany w obu kierunkach, ale tak, aby zachować proporcje i wypełnić całe okno

86 Problem z ROZSZERZONYM polega na tym, że obraz może być zdeformowany, co jest dużym problemem ze zdjęciami. Poniżej znajduje się okno, w którym wybrano SCALEDTOFIT: Oto obraz jest skalowany, więc wszystko może znajdować się w oknie, a następnie wyświetla się cały obraz. W rezultacie część okna nie może być używana. Metoda paintcomponent () została zmodyfikowana: class Drawing extends JComponent public void paintcomponent(graphics g) if (image!= null) Dimension d = getsize(); Graphics2D g2d = (Graphics2D)g; AffineTransform trans = new AffineTransform(); PaGUI.scale(trans, image, d, action); g2d.drawimage(image, trans, this); else super.paintcomponent(g);

87 i ważna jest metoda PaGUI.scale (), która zmienia obiekt trans odpowiadający wartości akcji: public static void scale(affinetransform trans, Image image, Dimension dim, ImageScaling action) if (action == ImageScaling.UNSCALED) return; double scalex = ((double)dim.width) / image.getwidth(null); double scaley = ((double)dim.height) / image.getheight(null); if (action == ImageScaling.SCALEDTOFIT) scalex = scaley = Math.min(scaleX, scaley); else if (action == ImageScaling.SCALEDTOFILL) scalex = scaley = Math.max(scaleX, scaley); trans.settoscale(scalex, scaley); Nie chcę pokazywać reszty programu, która dotyczy przede wszystkim interfejsu użytkownika i procedur obsługi zdarzeń. Metoda drawimage () rysuje obraz, który w zasadzie można manipulować w taki sam sposób, jak każdy inny obiekt Shape. Rozważ następujące okno, w którym na obrazie zapisany jest tekst: Kod komponentu jest następujący: class Drawing extends JComponent

88 public void paintcomponent(graphics g) Image image = PaGUI.createImageIcon("/drawimage/images/svane.jpg").getImage(); if (image!= null) Dimension d = getsize(); Graphics2D g2d = (Graphics2D)g; AffineTransform trans = new AffineTransform(); PaGUI.scale(trans, image, d, ImageScaling.SCALEDTOFILL); g2d.drawimage(image, trans, this); g2d.setfont(new Font("Serif", Font.BOLD, 272)); g2d.drawstring("abc", 15, 300); else super.paintcomponent(g); Jako kolejny przykład poniższy program rysuje ten sam obraz, ale tym razem obszar klipu jest definiowany za pomocą tekstu. W rezultacie widzisz tylko wodę i trochę ptaka: class Drawing extends JComponent

89 public void paintcomponent(graphics g) Image image = PaGUI.createImageIcon("/clipimage/images/svane.jpg").getImage(); if (image!= null) Dimension d = getsize(); Graphics2D g2d = (Graphics2D)g; AffineTransform trans = new AffineTransform(); PaGUI.scale(trans, image, d, ImageScaling.SCALEDTOFILL); g2d.setclip(getclipping(g2d)); g2d.drawimage(image, trans, this); else super.paintcomponent(g); private Shape getclipping(graphics2d g2d) Font font = new Font("Serif", Font.BOLD, 272); GlyphVector glyphs= font.createglyphvector(g2d.getfontrendercontext(), "ABC"); return glyphs.getoutline(15, 300); 7.1 OBRAZOWANIE Aplikacje do edycji obrazów, takie jak GIMP i Photoshop, zostały zaprojektowane do manipulowania zdjęciami cyfrowymi, a Java2D faktycznie zawiera funkcje przetwarzania obrazu, a te funkcje są przedmiotem poniższych. Teraz żaden z nowych konkurentów nie rozwija się nagle dla powyższych programów (są to wyjątkowo złożone programy) i jest więcej niż to, co bezpośrednio udostępnia Java2D, ale poniższe wyjaśnienia można, jeśli nic innego, wyjaśnić, jak działają profesjonalne programy, takie jak Photoshop i GIMP. Przetwarzanie obrazu opisuje, w jaki sposób można manipulować obrazami cyfrowymi za pomocą procesu zwanego zwykle filtrowaniem. Chodzi o to, że obraz o nazwie źródło jest przesyłany przez filtr, a wynikiem jest nowy obraz, zwany miejscem docelowym. Zarówno źródło, jak i miejsce docelowe są w Java2D reprezentowane przez BufferedImage. Filtr jest zdefiniowany przez interfejs o nazwie BufferedImageOp, a główną metodą definiowaną przez ten interfejs jest filter (). Klasa, która implementuje ten interfejs jest często nazywana operatorem obrazu. Java2D definiuje pięć takich operatorów obrazów:

90 1. ConvolveOp, używany do rozmywania, ostrzenia i znakowania krawędzi 2. AffineTransformOp, stosowany do transformacji geometrycznej 3. LookupOp, używany do redukcji kolorów i odwracania kolorów 4. RescaleOp, używany do rozjaśniania lub przyciemniania obrazu 5. ColorConvertOp, służy do konwersji przestrzeni kolorów Możesz oczywiście również zdefiniować własne operatory obrazu, które są tylko klasą, która implementuje BufferedImageOp. ConvolveOp Jest to filtr, który modyfikuje każdy piksel w źródle, więc odpowiadający mu piksel jest zmieniany z wartości sąsiednich pikseli. Odbywa się to za pomocą tak zwanego jądra, które określa matrycę z pikselami, które mają być użyte. Przykładem może być: gdzie piksel ma 8 sąsiednich pikseli. Każdy z elementów macierzy ma wartość, a 0 oznacza, że dany piksel nie przyczynia się do wyniku, natomiast 1 oznacza, że piksel jest przenoszony bez zmian do miejsca docelowego. Powyższe jądro jest więc identyfikatorem i definiuje filtr, który nie wpływa na miejsce docelowe. Jeśli chcesz zachować warunki oświetlenia, suma elementów macierzy musi wynosić 1. Jeśli suma jest mniejsza niż 1, obraz ciemnieje, a suma jest większa niż 1, obraz staje się jaśniejszy. Składnia do tworzenia obiektu ConvolveOp jest następująca public ConvolveOp(Kernel kernel, int edgehint) Oto ostatni parametr stała, która wskazuje, co powinno się stać z krawędziami, gdzie niekoniecznie znajdują się wszystkie sąsiadujące piksele. Istnieją dwie opcje: 1. publiczny statyczny finał w EDGE_ZERO_FILL, wskazujący, że piksele na krawędzi są ustawione na 0, a zatem stają się czarnymi pikselami w miejscu docelowym 2. public static final int EDGE_NO_OP, co oznacza, że piksele na krawędzi pozostają niezmienione w miejscu docelowym Operacje te są zwykle używane do ostrzejszego obrazu lub odwrotnie, jak to zwykle nazywa się rozmycie. Wynik zależy oczywiście od wartości matrycy, ale także od jej wielkości, i chociaż w profesjonalnym programie do edycji zdjęć można określić te wartości, istnieje wiele standardowych przykładów, na przykład 0,11 0,11 0,11 0,11 0,11 0,11

91 0,11 0,11 0,11 który zachowuje warunki oświetleniowe i skutkuje operacją rozmycia, a jako kolejny przykład można użyć następujących elementów, aby uzyskać ostrzejszy obraz: AffineTransformOp Jest to operacja, która wykonuje geometryczną transformację obrazu, ale taka, że operacja działa na każdym pikselu obrazu. W zależności od transformacji może to oznaczać, że obraz jest lekko zdeformowany (zwłaszcza obroty) - piksel niekoniecznie jest taki sam w źródle i miejscu docelowym. Wykorzystywane są dwa różne algorytmy, które można określić za pomocą parametru: 1. Najbliższy sąsiad, gdzie kolor każdego piksela w miejscu docelowym jest oparty na sąsiadujących pikselach w źródle. Jest to najbardziej efektywny z dwóch algorytmów. 2. Interpolacja dwuliniowa, w której kolory każdego piksela są określane przez połączenie kolorów pikseli w źródle, które nakładają się na siebie w miejscu docelowym. Ten algorytm jest mniej wydajny, ale zapewnia lepszy wynik. LookupOp Jest to operacja, która często może być używana jako alternatywa dla ConvolveOp, która jest łatwiejsza do zdefiniowania (łatwiej jest przewidzieć efekt). Oto tak zwana tabela odnośników, która w postaci tablicy zawiera kolor piksela w miejscu docelowym. Indeks tej tablicy jest wartością odpowiedniego piksela w źródle, a zatem kolor piksela w miejscu docelowym jest określany na podstawie odnośnika w tabeli. Istnieje wyszukiwanie dla każdej z trzech wartości: czerwonej, zielonej i niebieskiej. Ponieważ dla każdego koloru jest 256 opcji, w poszczególnych tablicach musi być miejsce na 256 wartości. Podczas tworzenia tabeli odnośników konieczne jest tylko określenie pojedynczej tablicy, a wartości w tablicy są następnie używane dla wszystkich trzech kolorów, ale można również określić trzy tablice. RescaleOp Jest to bardzo prosta operacja, która po prostu zwielokrotnia wszystkie wartości kolorów przez współczynnik. Aplikacji może nie być tak wiele, ale operację można na przykład wykorzystać do rozjaśnienia obrazu lub odwrotnie. Należy zauważyć, że oznacza to, że wszystkie wartości są pomnożone przez ten sam współczynnik i że niektóre wartości mogą następnie przekroczyć 256 (lub 0), dzięki czemu kolory stają się białe lub czarne. Możliwe jest również określenie przesunięcia, które jest wartością dodaną do wartości koloru.

92 ColorConvertOp Ostatnia operacja nie jest tutaj pokazana, ale służy do konwersji kolorów z jednej przestrzeni kolorów na drugą. Aby przetestować niektóre z powyższych w praktyce, możesz przestudiować program ImageEditor. Jeśli otworzysz aplikację, możesz otworzyć obraz, który możesz edytować za pomocą operatorów opisanych powyżej: Po lewej stronie masz wszystkie operacje, które możesz wykonać, podczas gdy prawa strona ma obraz. Obraz jest składnikiem następującego typu: class Drawing extends JComponent private BufferedImage image = null; public BufferedImage getimage() return image; public void setimage(bufferedimage image) this.image = image;

93 public void paintcomponent(graphics g) if (image!= null) Graphics2D g2d = (Graphics2D)g; AffineTransform trans = new AffineTransform(); g2d.drawimage(image, trans, this); setpreferredsize(new Dimension(image.getWidth(null), image.getheight(null))); else super.paintcomponent(g); Dimension d = getsize(); setpreferredsize(new Dimension(d.width, d.height)); który reprezentuje obraz jako BufferedImage. W przeciwnym razie najważniejsza jest oczywiście metoda paintcomponent (), która rysuje obraz i oblicza preferowany rozmiar komponentu. Lewa strona to pole listy, a każdy element (blisko dwóch) ma typ ImageOperation, czyli następującą klasę: private class ImageOperation implements Comparable<ImageOperation> private String name; private BufferedImageOp operation; public ImageOperation(String name, BufferedImageOp operation) this.name = name; this.operation = operation; public BufferedImageOp getoperation()

94 return operation; public String tostring() return name; public int compareto(imageoperation iop) return name.compareto(iop.tostring()); jest to klasa wewnętrzna w MainWindow. Klasa ma dwie zmienne, z których pierwsza to nazwa wyświetlana w polu listy, a druga to rzeczywista operacja przetwarzania obrazu w postaci obiektu BufferedImageOp, a zatem operacja wykonywana, gdy użytkownik kliknie obiekt w polu listy. Procedura obsługi zdarzenia dotycząca kliknięcia elementu w polu listy jest następująca: class MouseHandler extends MouseAdapter public void mouseclicked(mouseevent e) try JList list = (JList)e.getSource(); int n = list.locationtoindex(e.getpoint()); ImageOperation operation = null; if (n == 3) operation = rotate(); else if (n == 4) operation = scale(); else operation = (ImageOperation)model.get(n); if (operation!= null)

95 BufferedImageOp opr = operation.getoperation(); if (opr instanceof LookupOp) opr.filter(drawing.getimage(), drawing.getimage()); else drawing.setimage(opr.filter(drawing.getimage(), null)); drawing.repaint(); catch (Exception ex) System.out.println(ex); to także klasa wewnętrzna. Przewodnik decyduje, który element zostanie kliknięty. Jeśli indeks wynosi 3 lub 4, otworzy się standardowe okno dialogowe, a użytkownik musi wprowadzić wartość (kąt obrotu lub wartość procentową, o ile obraz ma zostać przeskalowany). W obu przypadkach zwracany jest obiekt ImageOperation do przetwarzania obrazu. Jeśli indeks nie jest równy 3 lub 4, rzut typu elementu klikanego elementu jest wykonywany dla obiektu ImageOperation. Po udostępnieniu obiektu określany jest obiekt BufferedImageOp i wykonywane jest odpowiednie przetwarzanie obrazu. W tym miejscu powinieneś zauważyć dwie rzeczy: Jak odwoływać się do samego obrazu i że przetwarzanie obrazu jest wykonywane przez metodę filter (), ale na dwa sposoby, w zależności od tego, która to operacja. Wówczas istniały pojedyncze obiekty BufferedImageOp, które wszystkie muszą zostać utworzone. Jako przykład poniżej pokazano metodę, która tworzy obiekt, aby obraz stał się ostrzejszy, a tym samym obiekt ConvolveOp: private ImageOperation createsharpen() float[] sharp = 0f, -1f, 0f, -1f, 5f, -1f, 0f, -1f, 0f ; return new ImageOperation("Skarpere", new ConvolveOp(new Kernel(3, 3, sharp))); Jedyne, na co należy zwrócić uwagę, to jak utworzyć obiekt jądra. Jako kolejny przykład poniżej pokazano, jak dodać obiekt LookupOp do pola listy: model.addelement(new ImageOperation("Remove red", new LookupOp(new ShortLookupTable(0, new short[][] zero, one, one ), null))); Oto filtr, który usuwa czerwony kolor. Dwie tablice: jedna i zero (do wyszukiwania) to tablice z 256 miejscami, gdzie wszystkie miejsca mają odpowiednio 1 i 0. W innym przykładzie poniżej znajduje się

96 przykład filtra RescaleOp, który skaluje wszystkie wartości kolorów do połowy, a tym samym przyciemnia obraz : model.addelement(new ImageOperation("Rescale 0.5, 0", new RescaleOp(.5f, 0, null))); Na koniec pokazano metodę, która tworzy obiekt AffineTransformOp, który skaluje obraz do 90 procent wielkości: private ImageOperation zoomout() AffineTransform trans = AffineTransform.getScaleInstance(0.9, 0.9); return new ImageOperation("Zoom out", new AffineTransformOp(trans, null)); Zachęcamy do przetestowania programu i zbadania wpływu różnych operacji. Jest daleki od nowego programu GIMP, ale może trochę wyjaśnić, jak działa program taki jak GIMP ĆWICZENIE 15 Utwórz kopię projektowanego ImageEditor. Trzy najważniejsze operacje na obrazie (rozmycie, wyostrzenie i wyróżnienie krawędzi) są typu ConvolveOp i filtra z matrycą 3 3. Dodaj kolejną operację (na przykład poniżej operacji Skaluj obraz), która jest również ConvolveOp, ale operacją wykorzystującą matrycę 5 5, którą można wprowadzić w oknie dialogowym pokazanym poniżej. Po dodaniu nowej operacji wypróbuj różne matryce i sprawdź efekt. Zauważ, że w sieci można znaleźć przykłady matryc, z którymi inni mieli szczęście. 7.2 BUFFEREDIMAGE W tej sekcji chciałbym dowiedzieć się nieco więcej o klasie BufferedImage, która jest klasą zapewniającą usługi niezbędne do manipulowania poszczególnymi pikselami na obrazie. BufferedImage jest centralną klasą przetwarzania obrazu i wywodzi się z klasy Image, a zatem obiekt typu BufferedImage może być użyty jako parametr metody drawimage (). Obraz, a zatem obiekt Image, to w zasadzie nic innego jak układ pikseli w różnych kolorach. Obraz buforowany można zilustrować w następujący sposób:

97 i zasadniczo składa się z rastra i modelu kolorów. Surowe dane są zapisywane jako tablice w części rastrowej, podczas gdy model kolorów określa, w jaki sposób dane powinny być interpretowane jako kolory. Każdy piksel na obrazie jest definiowany przez jedną lub więcej wartości, zwanych próbkami. Na przykład ma czarno-biały obraz po jednej próbce na każdy piksel, podczas gdy obraz RGB ma 3 próbki na każdy piksel. Bufor zawiera bieżące próbki przechowywane w tablicach tablic bajtowych lub całkowitych, a część rastrowa ma również model przykładowy, który określa sposób organizacji bufora i sposób odwoływania się do określonego piksela w buforze. Model kolorów interpretuje próbki poszczególnych pikseli jako kolory. Czy jest to obraz czarno-biały z jedną próbką dla każdego piksela, jest interpretowany jako skala szarości między czernią a bielą, a na obrazie RGB model kolorów wykorzystuje wszystkie trzy próbki dla jednego piksela i interpretuje je jako intensywność czerwieni, zieleni i niebieski w kolorze RGB. Wszystkie próbki dla określonego koloru nazywane są pasmem lub kanałem. Zatem obraz RGB ma na ogół 3 kanały, ale może również mieć czwarty dla wartości alfa. Rozważmy na przykład obraz jpg o wymiarach 4920 x 2768 pikseli. Ponieważ istnieje obraz RGB, dla każdego piksela są przechowywane trzy próbki w trzech bajtach, każda z miejscem na bajtów. Każda tablica jest uporządkowana w kolejności 2768 wierszy, a każdy wiersz wykorzystuje 4920 miejsc. Istnieje wiele szczegółów dotyczących BufferedImage i istnieją klasy dla wszystkich powyższych składników. Rzadko konieczna jest bezpośrednia praca z tymi klasami, a ja chcę tylko pokazać mały przykład, który pokazuje, jak bezpośrednio utworzyć obraz za pomocą BufferedImage. Pełny kod wygląda następująco: package createpicture; import java.io.*; import javax.imageio.*; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.util.*; import javax.swing.*; import javax.swing.border.*; public class MainWindow extends JFrame

98 private Drawing drawing; public MainWindow() super("createpicture"); setlocationrelativeto(null); createwindow(); setdefaultcloseoperation(exit_on_close); pack(); setvisible(true); private void createwindow() JPanel panel = new JPanel(new BorderLayout(0, 20)); panel.setborder(new EmptyBorder(20, 20, 20, 20)); panel.add(createbottom(), BorderLayout.SOUTH); panel.add(drawing = new Drawing()); add(panel); private JPanel createbottom() JButton cmd1 = new JButton("Tegn billede"); cmd1.addactionlistener(new ActionListener() public void actionperformed(actionevent e) drawing.draw(); ); JButton cmd2 = new JButton("Gem billede"); cmd2.addactionlistener(new ActionListener() public void actionperformed(actionevent e) try File file = new File("billede.jpg"); ImageIO.write(drawing.getImage(), "jpg", file); catch (Exception ex) ;

99 ); JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); panel.add(cmd2); panel.add(cmd1); return panel; class Drawing extends JComponent private static final int WIDTH = 500; private static final int HEIGHT = 300; private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); public Dimension getpreferredsize() return new Dimension(WIDTH, HEIGHT); public BufferedImage getimage() return image; public void paintcomponent(graphics g) super.paintcomponent(g); Graphics2D g2d = (Graphics2D)g; g2d.drawimage(image, 0, 0, this); public void draw()

100 int color = 0x00FF0000; for (int j = 0; j < 10; ++j) for (int i = 20; i < image.getwidth() 20; ++i) image.setrgb(i, 20 + j, color); for (int j = image.getheight() 30; j < image.getheight() 20; ++j) for (int i = 20; i < image.getwidth() 20; ++i) image.setrgb(i, j, color); for (int j = 30; j < image.getheight() 30; ++j) for (int i = 20; i < 30; ++i) image.setrgb(i, j, color); for (int j = 30; j < image.getheight() 30; ++j) for (int i = image.getwidth() 30; i < image.getwidth() 20; ++i) image.setrgb(i, j, color); int[] arr = new int[20000]; Arrays.fill(arr, 0x000000FF); image.setrgb(150, 100, 200, 100, arr, 0, 200); repaint(); Jeśli uruchomisz program, zobaczysz okno na następnej stronie. Okno ma BorderLayout, w którym u dołu znajduje się panel z dwoma przyciskami, podczas gdy środek jest składnikiem rysunku, który rysuje obraz. Obraz jest BufferedImage: private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); Klasa ma więcej konstruktorów, ale w tym przypadku do opisania rozmiaru obrazu i jego modelu kolorów użyto konstruktora. W tym przypadku jest to trzykanałowy model RGB, ale klasa BufferedImage definiuje stałe dla wielu innych modeli kolorów. W rezultacie w tym przypadku tworzony jest obraz o wymiarach 500 x 300 pikseli. Obraz jest rysowany w paintcomponent (), a gdy wynikiem jest czarny obraz, dzieje się tak, ponieważ wszystkie przykładowe dane to 0, a zatem bufor obrazu składa się z trzech tablic (o długości ), gdzie wszystkie wartości wynoszą 0.

101 Jeśli klikniesz przycisk Rysuj obraz, metoda draw() zostanie wykonana na rysunku klasy. Zmienia niektóre piksele za pomocą metody setpixel(). Jest dostępny w dwóch wersjach, z których jedna modyfikuje pojedynczy piksel, a druga modyfikuje prostokątny obszar pikseli w tablicy. Wynik jest taki, jak pokazano poniżej: Oczywiście nie jest tak często, że istnieje potrzeba pracy z obrazem na tym poziomie, ale przykład pokazuje, że używając BufferedImage, możesz stworzyć obraz od podstaw i częściowo jak zmodyfikować poszczególne piksele. Ostatni przycisk służy do zapisania obrazu jako obrazu jpg, a następnie można go użyć jako dowolnego innego obrazu jpg. 7.3 EKRAN Podczas pracy z grafiką i obrazami może być konieczne określenie rozdzielczości monitora. Można to zrobić na kilka sposobów, ale następujący program pokazuje przykład: package screensize; import java.awt.*;

102 public class ScreenSize public static void main(string[] args) Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); double width = screensize.getwidth(); double height = screensize.getheight(); System.out.println(width + " x " + height); Nie ma wiele do wyjaśnienia, poza odnotowaniem, że w ten sposób można uzyskać rozdzielczość ekranu 8 ANIMACJI Ponieważ ostatni temat dotyczy Java2D, przyjrzę się, jak pracować z animacjami w programie. W rzeczywistości Java2D nie obsługuje animacji, a wszystko, co możesz zrobić, to symulować animację postaci, rysując ją nieco później i być może w nowej pozycji. Robienie tego często (wiele razy na sekundę) daje efekt przypominający animację. Program AnimationDemo pokazuje trochę o tym, co robić. Jeśli otworzysz program, pojawi się następujące okno: Nie wspomnę o kodzie tego okna, ponieważ są to tylko trzy przyciski, a każdy przycisk otwiera okno dialogowe. Pierwsze okno dialogowe symuluje animację figury geometrycznej, następne animacje tekstu, a ostatnie symuluje animację obrazu. Kliknięcie górnego przycisku powoduje wyświetlenie okna poniżej, które pokazuje animację stosunkowo złożonej figury geometrycznej:

103 U góry znajduje się pasek narzędzi, który zawiera pięć pól wyboru wskazujących, czy 1. podczas rysowania figury stosuje się antyaliasing 2. transformacja jest wykonywana jako obrót wokół środka 3. używana jest farba gradientowa (jak w powyższym przypadku) 4. wokół figury rysowany jest obwód 5. gdzie użyć obszaru klipu (są rysowane jako tekst) Po prawej stronie znajdują się dwa przyciski do uruchamiania i zatrzymywania animacji, a na końcu jest suwak służący do regulacji prędkości animacji. Oprócz tego paska narzędzi, okno dialogowe zawiera JComponent, który jest komponentem symulującym animację. Nie chcę wyświetlać kodu okna dialogowego, ale komponent z rysunkiem jest zdefiniowany jako klasa wewnętrzna w następujący sposób: class Picture extends JComponent private float x; private float y; private float deltax = rand.nextfloat(); private float deltay = rand.nextfloat(); private float width; private float height; private float theta = 0; private BufferedImage image;

104 private javax.swing.timer timer; public Picture(BufferedImage image, int delay) this.image = image; width = this.image.getwidth(); height = this.image.getheight(); x = rand.nextfloat() * width; y = rand.nextfloat() * height; addcomponentlistener(new ResizeListener()); timer = new javax.swing.timer(delay, this::tick); public void settimer(int time) timer.setdelay(time); public void start() timer.start(); public void stop() timer.stop(); private void tick(actionevent e) Dimension d = getsize(); if (x + deltax < 0) deltax = -deltax;

105 else if (x + width + deltax >= d.width) deltax = -deltax; if (y + deltay < 0) deltay = -deltay; else if (y + height + deltay >= d.height) deltay = -deltay; x += deltax; y += deltay; if (transform) theta += Math.PI / 200; if (theta > 2 * Math.PI) theta -= (2 * Math.PI); repaint(); public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; settransform(g2d); g2d.drawimage(image, AffineTransform.getTranslateInstance(x, y), null); private void settransform(graphics2d g2) if (transform == false) return; Dimension d = getsize(); g2.rotate(theta, d.width / 2, d.height / 2); class ResizeListener extends public void componentresized(componentevent ce) Dimension d = getsize(); if (x < 0) x = 0; else if (x + width >= d.width) x = d.width width 1; if (y < 0) y = 0; else if (y + height >= d.height) y = d.height height 1;

106 Zauważ najpierw, że klasa dziedziczy JComponent, a zatem jest komponentem. Znaczenie zmiennych klasy powinno być zrozumiałe, ale zwróć uwagę na licznik czasu, który jest silnikiem animacji. Kształty są tworzone za pomocą metody createshape () i tworzy odcinki GeneralPath, które są krzywymi sześciennymi określonymi przez punkty w punkcie tablicy. Zasadą jest, że za każdym razem, gdy zegar tyka, rysunek jest rysowany, ale najpierw po nieznacznej modyfikacji punktów za pomocą wartości z tablicy delta. Zarówno punkty, jak i delta są inicjowane w konstruktorze, ale tutaj nie ma wiele do zapamiętania poza dodaniem modułu obsługi zdarzeń do zmiany rozmiaru okna. Przewodnik powinien upewnić się, że współrzędne kształtu mieszczą się w oknie po zmianie rozmiaru. Moduł obsługi zdarzeń licznika czasu nazywa się tick () i modyfikuje punkty na rysunku, a kończy odświeżeniem (). W rezultacie okno jest odmalowywane za każdym razem, gdy odmierza czas. Oznacza to, że wykonywana jest funkcja paintcomponent (). Tutaj należy zauważyć, że wywołuje szereg metod pomocniczych, które ustawiają obiekt graficzny dla wybranych pól wyboru. Zauważ też, że metoda tworzy figurę, wywołując metodę createshap (), rysując w ten sposób nowe współrzędne. Klikając w głównym oknie pozostałych dwóch przycisków, otrzymujesz wynik, który animuje tekst i obraz. Nie będę wyświetlać kodu dla tych komponentów, ponieważ są one w zasadzie identyczne z powyższymi, ale zachęcamy do wypróbowania programu, a zwłaszcza podczas animacji obrazu może być trudne do narysowania przez program obraz wystarczająco szybki. Może to być spowodowane obecną kartą graficzną, ale przede wszystkim dlatego, że metoda drawimage () nie ma pożądanej wydajności. 10 DRUKOWANIE W tej części opiszę jak z programu do pisania do drukarki. W naszym społeczeństwie bez dokumentów papierowych nie odgrywa takiej samej roli jak wcześniej, ale są jednak programy, w których również chcesz drukować wyniki na papierze, więc ten rozdział. Wraz z rozwojem Javy pisanie na drukarce fizycznej stało się łatwiejsze, a między innymi kilka komponentów Swing ma wbudowane funkcje drukowania, ale jeśli chcesz mieć pełną kontrolę nad sposobem drukowania danych, niektóre szczegóły są powiązane z zadaniem. Rozdział, podobnie jak reszta tej książki, składa się z wielu przykładów tego, co musisz napisać. Chcę zacząć od aplikacji, która otwiera następujące okno: i klikając przycisk, pojawi się następujące okno dialogowe:

107 które jest domyślnym oknem dialogowym Java do wyboru drukarki i ustawień. Jeśli klikniesz Drukuj, wydrukujesz pojedynczą stronę z tekstem Witaj świecie. Kod głównego widoku programu to: package helloprinter; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.awt.print.*; public class MainView extends JFrame implements Printable public MainView() super("hello printer"); setsize(200, 100); setlayout(new FlowLayout()); JButton cmdprint = new JButton("Print"); cmdprint.addactionlistener(this::print); add(cmdprint); setlocationrelativeto(null);

108 setdefaultcloseoperation(exit_on_close); setvisible(true); public int print(graphics g, PageFormat pf, int page) throws PrinterException if (page > 0) return NO_SUCH_PAGE; Graphics2D g2d = (Graphics2D)g; g2d.translate(pf.getimageablex(), pf.getimageabley()); g.drawstring("hello world!", 100, 100); return PAGE_EXISTS; public void print(actionevent e) PrinterJob job = PrinterJob.getPrinterJob(); job.setprintable(this); if (job.printdialog()) try job.print(); catch (PrinterException ex) JOptionPane.showMessageDialog(this, ex.tostring()); Jeśli chodzi o tworzenie okna, nie ma nic nowego, ale należy zauważyć, że klasa implementuje interfejs Printable i ogólnie można drukować obiekty, które implementują ten interfejs. Interfejs definiuje tylko jedną metodę o nazwie print () i jest to metoda zapisująca się do drukarki.

109 Patrząc na moduł obsługi zdarzeń przycisku Drukuj, tworzy obiekt PrinterJob, a jak sama nazwa wskazuje, jest to obiekt reprezentujący zadanie kufelkowania. Następnie obiekt, który ma zostać wydrukowany - w tym przypadku - jest powiązany z metodą printdialog (). Jest to metoda, która otwiera powyższe okno dialogowe drukowania. Jeśli zwraca wartość true odpowiadającą kliknięciu przycisku Drukuj, metoda print () jest wykonywana na obiekcie zadania, co z kolei oznacza, że metoda public int print(graphics g, PageFormat pf, int page) throws PrinterException jest wykonywane. Pierwszy parametr to grafika do narysowania, a drugi to format strony (zdefiniowany w oknie dialogowym), a ostatni parametr to numer strony. Format określa między innymi obszar klipu, a następująca instrukcja tłumaczy grafikę do lewego górnego rogu obszaru klipu: g2d.translate (pf.getimageablex (), pf.getimageabley ()); po czym tekst jest rysowany jak każdy inny tekst na obiekcie graficznym. Następny przykład nazywa się PrintPages i jest przykładem drukowania wielu stron. Program ma dokładnie taki sam interfejs użytkownika, jak w pierwszym przykładzie, więc pokażę tylko kod związany z drukowaniem, ale najpierw następującą metodę: private ArrayList<ArrayList<String>> createpages(int lines, int size) ArrayList<ArrayList<String>> pages = new ArrayList(); ArrayList<String> page = new ArrayList(); for (int n = 1; n <= lines; ++n) page.add("this is line number " + n); if (page.size() == size) pages.add(page); page = new ArrayList(); if (page.size() > 0) pages.add(page); return pages; który tworzy ArrayList z obiektami typu ArrayList <String> i symuluje ArrayList ze stronami tekstowymi. Dwa parametry wskazują odpowiednio całkowitą liczbę linii i liczbę linii dla każdej strony. Metoda jest trywialna i nie wymaga specjalnego wyjaśnienia.

110 Procedura obsługi przycisku Drukuj jest taka sama, jak w pierwszym przykładzie i nie została tutaj pokazana, więc jest to metoda print (), w której jest coś do odnotowania: public int print(graphics g, PageFormat pf, int page) throws PrinterException int height = g.getfontmetrics(font).getheight(); if (pages == null) pages = createpages(200, (int)(pf.getimageableheight() / height)); if (page >= pages.size()) return NO_SUCH_PAGE; Graphics2D g2d = (Graphics2D)g; g2d.translate(pf.getimageablex(), pf.getimageabley()); for (int y = 0, i = 0; i < pages.get(page).size(); ++i) y += height; g.drawstring(pages.get(page).get(i), 0, y); return PAGE_EXISTS; Najpierw określana jest wysokość użytej czcionki, a tym samym wysokość linii, która jest używana w wywołaniu metody createpages () w celu ustalenia liczby linii na stronie. Obiekt PageFormat definiuje obszar klipu, który jest obszarem zwanym Imageable, a do określenia liczby linii używana jest wysokość tego obszaru. Ponieważ tym razem drukowanych jest wiele stron, metoda print () jest wywoływana odpowiednią liczbę razy i należy zauważyć, że metoda createpages () jest wywoływana tylko raz. Metoda print () powinna zwrócić NO_SUCH_PAGE, jeśli nie ma więcej stron, w przeciwnym razie metoda zwróci PAGE_EXISTS. Reszta metody polega na wydrukowaniu linii dla pojedynczej strony, do której odnosi się strona zmienna, która określa numer strony, ale w przeciwnym razie metoda składa się tylko z pętli, która zapętla wszystkie linie i gdzie zmienna y zachowuje śledzić, gdzie wydrukować. Innym przykładem jest PrintOptions i jest prawie identyczny z pierwszym przykładem i drukuje tekst Witaj świecie. Jedyną różnicą jest procedura obsługi przycisku Drukuj, która pokazuje, że można otworzyć okno dialogowe z ustawieniami drukarki i drukować bez zwykłego okna dialogowego wyboru drukarki. public void print(actionevent e) PrinterJob job = PrinterJob.getPrinterJob(); PrintRequestAttributeSet attr = new HashPrintRequestAttributeSet(); PageFormat format = job.pagedialog(attr); job.setprintable(this, format); try job.print(attr); catch (PrinterException ex)

111 JOptionPane.showMessageDialog(this, ex.getmessage()); Następny przykład nazywa się PrintWindow i otwiera następujące okno: i jest zatem zasadniczo tym samym programem, co pierwszy przykład w tej książce, a przykład powinien przede wszystkim wykazać, że można wydrukować dowolną figurę graficzną na drukarce i że odbywa się to w taki sam sposób, jak drukuje się figurę na ekranie. Kod jest zasadniczo taki sam, jak program HelloJava2D, z wyjątkiem tego, co jest wymagane dla przycisku, a klasa implementuje Printable. Ta figura jest podobnie komponentem JC zwanym Rysunkiem. Procedura obsługi przycisku jest identyczna z procedurą obsługi zdarzenia w powyższym przykładzie, dlatego należy przede wszystkim zwrócić uwagę na metodę print () i sposób natychmiastowego wydrukowania składnika JComponent: public int print(graphics g, PageFormat pf, int page) throws PrinterException if (page > 0) return NO_SUCH_PAGE; Graphics2D g2d = (Graphics2D)g; g2d.translate(pf.getimageablex(), pf.getimageabley()); drawing.paintall(g); return PAGE_EXISTS; 10.1 ELEMENTY SWING

112 Patrząc na powyższe przykłady, generalnie łatwo jest napisać do drukarki, ale ostatecznie to programista odpowiada, że to, co jest drukowane, może znajdować się na papierze i odpowiednio podzielone na strony. To właśnie może sprawić, że drukowanie dokumentów będzie skomplikowane, ale kilka komponentów Swing ma wbudowane funkcje drukowania treści, a oto dwa główne przykłady JTable i JTextComponent. Program AJTable otwiera następujące okno: i kliknięcie przycisku Drukuj spowoduje wyświetlenie okna dialogowego drukowania i możesz wydrukować zawartość tabeli. Kod jest następujący, w którym nie pokazałem modelu danych dla tabeli package ajtable; import java.net.*; import java.awt.*; import java.awt.event.*; import java.awt.print.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.able.*; public class MainView extends JFrame private JTable table = new JTable(new Kings()); public MainView() super("a JTable"); setsize(400, 300);

113 createview(); setlocationrelativeto(null); setdefaultcloseoperation(exit_on_close); setvisible(true); private void createview() JPanel panel = new JPanel(new BorderLayout()); panel.setborder(new EmptyBorder(5, 5, 5, 5)); setcolumnwidth(table.getcolumnmodel().getcolumn(1), 100); setcolumnwidth(table.getcolumnmodel().getcolumn(2), 100); panel.add(new JScrollPane(table)); JButton cmdprint = new JButton("Print"); cmdprint.addactionlistener(this::print); JPanel command = new JPanel(new FlowLayout()); command.add(cmdprint); panel.add(command, BorderLayout.SOUTH); add(panel); private void setcolumnwidth(tablecolumn col, int width) col.setpreferredwidth(width); col.setminwidth(width); col.setmaxwidth(width); public void print(actionevent e) try table.print();

114 catch (PrinterException ex) JOptionPane.showMessageDialog(this, ex.tostring()); W rzeczywistości nie ma wiele do wyjaśnienia, a konstrukcja jest dość prosta. Należy zauważyć, że klasa nie implementuje opcji Drukowalne i że moduł obsługi zdarzeń przycisku wykonuje tylko następującą instrukcję: table.print (); Instrukcja otwiera okno dialogowe drukowania i drukuje zawartość tabeli. Metoda print () automatycznie utworzy wymaganą liczbę stron. Metodę tę można znaleźć w kilku przesłonięciach, których parametry obejmują na przykład nagłówek i stopkę, a wydrukowanie JTabel jest dość proste. Jako kolejny przykład pokażę program AEditorPane, który otwiera następujące okno: Program pokazuje początek Sagi Egila w JEditorPane. Dokument jest dokumentem HTML. Kliknięcie przycisku Drukuj powoduje wydrukowanie dokumentu, a tym samym zawartości komponentu JEditorPane, i ładnie sformatowanego i podzielonego na wymaganą liczbę stron. Wszystko dzieje się dość automatycznie, jak w poprzednim programie. Nie chcę tutaj wyświetlać kodu, ponieważ w druku nie ma nic nowego PRINTSERVICES Interfejs API drukowania w Javie ewoluuje w sposób ciągły, w którym pierwsze wersje Java nie obsługują drukowania, a więc w zaawansowany interfejs API z wieloma funkcjami do inicjowania zadań drukowania. Jak pokazują powyższe przykłady, pisanie na drukarce jest dziś proste, ale pisząc program, nie wiadomo, która drukarka znajduje się na drugim końcu, a ponadto może być dostępnych wiele drukarek z wieloma różnymi udogodnieniami. Aby obsłużyć te opcje, Java została rozszerzona o PrintServices, a poniżej znajduje się krótkie wprowadzenie do tego, czym jest.

115 Zasadą w pracy drukowania są nowe możliwości: 1. wybierz drukarkę reprezentowaną przez obiekt PrintService 2. utwórz zadanie reprezentowane przez obiekt DocPrintJob 3. utwórz obiekt Doc, który reprezentuje dane do wydrukowania 4. rozpocznij zadanie, wywołując metodę print () obiektu DocPrintJob Aby wybrać drukarkę, używasz metod statycznych w klasie PrintServiceLookup: package printservicesprogram; import javax.print.*; import javax.print.attribute.*; import javax.print.attribute.standard.*; public class PrintServicesProgram public static void main(string[] args) PrintService[] services = PrintServiceLookup.lookupDefaultPrintService() ; print(services); print(printservicelookup.lookupprintservices(null, null)); print(printservicelookup.lookupprintservices(docflavor.url.text_html_utf_8, null)); AttributeSet attrs = new HashAttributeSet(); attrs.add(colorsupported.supported); attrs.add(orientationrequested.landscape); attrs.add(javax.print.attribute.standard.mediasizename.iso_a3); print(printservicelookup.lookupprintservices(null, attrs)); PrintRequestAttributeSet attr = new HashPrintRequestAttributeSet(); PrintService service = ServiceUI.printDialog(null, 100, 100, PrintServiceLookup.lookupPrintServices(null, null), PrintServiceLookup.lookupDefaultPrintService(), null, attr); if (service!= null) print(new PrintService[] service ); Attribute[] arr = attr.toarray();

116 for (Attribute a : arr) System.out.println(a.getName() + ": " + a); private static void print(printservice[] services) for (int i = 0; i < services.length; ++i) System.out.println(services[i].getName()); System.out.println(); Klasa PrintServiceLookup ma metodę lookupprintservices (), która zwraca tablicę dostępnych obiektów PrintService i będą to wszystkie znalezione drukarki. Korzystając z metody bez parametrów, po prostu otrzymujesz wszystkie drukarki, które zna komputer, ale za pomocą dwóch parametrów możesz określić, które drukarki zwrócić. Te dwa parametry to odpowiednio DocFlavor i AttributeSet, gdzie pierwszy wskazuje typ MIME, a tym samym jakiego rodzaju dokumenty powinna drukować drukarka, a drugi wskazuje, jakie inne funkcje powinna obsługiwać drukarka. Jako przykład z powyższego kodu poniższa instrukcja zwróci wszystkie drukarki, które mogą drukować kodowane w formacie UTF8 HTML: PrintServiceLookup.lookupPrintServices(DocFlavor.URL.TEXT_HTML_UTF_8, null) ako inny przykład, poniższa instrukcja zwraca wszystkie drukarki, które mają właściwości zdefiniowane przez ostatni parametr: PrintServiceLookup.lookupPrintServices (null, attrs) w tym przypadku byłaby to drukarka kolorowa, która powinna obsługiwać LANDSCAPE, i powinna to być drukarka A3. Aby wybrać drukarkę, możesz również użyć ServiceUI.printDialog (), która otwiera zwykłe okno dialogowe drukowania. Pierwsze trzy parametry wskazują okno nadrzędne i położenie okna dialogowego względem tego okna. Zwróć również uwagę na ostatni parametr, którym jest PrintRequestAttributeSet, który zwraca właściwości obsługiwane przez wybraną drukarkę. Zachęcamy do przetestowania programu na swoim komputerze. Ponadto zachęcamy do zapoznania się z pomocą dotyczącą tego, jakie stałe i typy są zdefiniowane, aby określić DocFlavor i AttributeSet i jest ich wiele. Na przykład pokażę program, w którym możesz wybrać obraz i wydrukować go. Program otwiera niestandardową przeglądarkę do systemu plików, w którym można wybrać obraz i wydrukować go na domyślnej drukarce. Kod programu jest następujący: package printimage; import java.io.*; import java.awt.*; import java.awt.print.*; import javax.swing.*; import javax.swing.filechooser.*;

117 import javax.imageio.*; import javax.print.*; public class PrintImage private ImageIcon icon = null; public static void main(string[] args) (new PrintImage()).printImage(); private void printimage() JFileChooser chooser = new JFileChooser(new File(System.getProperty("user.home"))); chooser.setfilefilter(new FileNameExtensionFilter("Picture", "jpg")); if (chooser.showopendialog(null) == JFileChooser.APPROVE_OPTION) icon = loadimage(chooser.getselectedfile()); if (icon!= null) try PrintService service = PrintServiceLookup.lookupDefaultPrintService(); DocPrintJob job = service.createprintjob(); DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE; SimpleDoc doc = new SimpleDoc(new Picture(), flavor, null); job.print(doc, null); catch (Exception ex)

118 private ImageIcon loadimage(file file) try return new ImageIcon(ImageIO.read(file)); catch (Exception ex) return null; class Picture implements Printable public int print(graphics g, PageFormat format, int page) if (page > 0) return Printable.NO_SUCH_PAGE; Graphics2D g2d = (Graphics2D)g; g2d.translate((int)(format.getimageablex()), (int)(format.getimageabley())); scalegraphics(g2d, format); g2d.drawimage(icon.getimage(), 0, 0, null); return Printable.PAGE_EXISTS; private void scalegraphics(graphics2d g2d, PageFormat format) double pagewidth = format.getimageablewidth(); double pageheight = format.getimageableheight(); double imagewidth = icon.geticonwidth(); double imageheight = icon.geticonheight();

119 double scalex = pagewidth / imagewidth; double scaley = pageheight / imageheight; double scale = Math.min(scaleX, scaley); g2d.scale(scale, scale); Wszystko dzieje się w metodzie printimage (), która otwiera okno dialogowe JFileChooser, w którym można wybrać plik jpg. Plik jest otwierany za pomocą metody loadimage (), która zwraca obraz jako ImageIcon. Jeśli obraz został otwarty, obiekt PrintService jest określany dla domyślnej drukarki: PrintService service = PrintServiceLookup.lookupDefaultPrintService (); i dla tego PrintService tworzony jest DocPrintJob: DocPrintJob job = service.createprintjob (); Aby wydrukować obraz, zdefiniowano obraz klasy wewnętrznej, który implementuje opcję drukowania, a tym samym stronę drukarki. Klasa wygląda jak poprzednie klasy do wydruku i musi implementować metodę print (), ale ponieważ cały obraz powinien znajdować się na stronie, grafika jest skalowana, dzięki czemu jest miejsce na cały obraz. Powinieneś zwrócić uwagę na sposób skalowania, ponieważ jest to zadanie, które jest często potrzebne. Dzięki tej klasie możesz zdefiniować dokument do wydrukowania: SimpleDoc doc = nowy SimpleDoc (nowy obraz (), smak, null); i tutaj należy szczególnie zwrócić uwagę na parametr smakowy, którego typ to DocFlavor.SERVICE_FORMATTED.PRINTABLE; co oznacza, że pojedyncza strona musi zostać wydrukowana w formacie obiektu do druku. Następnie pokażę program o nazwie PrintBook, który drukuje trzy strony: package printbook; import java.awt.*; import java.awt.font.*; import java.awt.print.*; import java.awt.geom.*; import javax.print.*; public class PrintBook

120 public static void main(string[] args) try PrintService service = PrintServiceLookup.lookupDefaultPrintService(); DocPrintJob job = service.createprintjob(); PageFormat format = new PageFormat(); Book book = new Book(); book.append(new Page0(), format); book.append(new Page1(), createformat1(format)); book.append(new Page2(), createformat2(format)); DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PAGEABLE; SimpleDoc doc = new SimpleDoc(book, flavor, null); job.print(doc, null); catch (Exception ex) private static PageFormat createformat1(pageformat df) PageFormat pf = (PageFormat) df.clone(); Paper paper = df.getpaper(); paper.setimageablearea(20, 20, df.getwidth() 40, df.getheight() 40); pf.setpaper(paper); return pf; private static PageFormat createformat2(pageformat df) PageFormat pf = (PageFormat) df.clone(); pf.setorientation(pageformat.landscape); return pf;

121 class Page0 implements Printable public int print(graphics g, PageFormat format, int page) throws PrinterException Graphics2D g2d = (Graphics2D) g; double width = format.getimageablewidth(); double height = format.getimageableheight(); double xpos = format.getimageablex(); double ypos = format.getimageabley(); Ellipse2D ellip = new Ellipse2D.Double( width / xpos, height / ypos, 100, 100); g2d.setcolor(color.red); g2d.fill(ellip); return Printable.PAGE_EXISTS; class Page1 implements Printable public int print(graphics g, PageFormat format, int page) throws PrinterException Graphics2D g2d = (Graphics2D) g; double width = format.getimageablewidth(); double height = format.getimageableheight(); double xpos = format.getimageablex(); double ypos = format.getimageabley(); Rectangle2D rect = new Rectangle2D.Double(xpos, ypos, width, height); Line2D line1 = new Line2D.Double(xpos, ypos, xpos + width, ypos + height); Line2D line2 = new Line2D.Double(xpos, ypos + height, xpos + width, ypos); g2d.setcolor(color.blue); g2d.draw(rect);

122 g2d.draw(line1); g2d.draw(line2); return Printable.PAGE_EXISTS; class Page2 implements Printable public int print(graphics g, PageFormat format, int page) throws PrinterException Graphics2D g2d = (Graphics2D) g; g2d.translate((int)(format.getimageablex()), (int)(format.getimageabley())); double width = format.getimageablewidth(); double height = format.getimageableheight(); TextLayout text = new TextLayout("Hello", new Font("Serif", Font.BOLD, 144), g2d.getfontrendercontext()); Rectangle2D rect = text.getbounds(); text.draw(g2d, (int)(width rect.getwidth()) / 2, (int)(height + rect.getheight()) / 2); return Printable.PAGE_EXISTS; Trzy strony są zdefiniowane jako klasy wewnętrzne i wszystkie są obiektami do wydruku. Pierwsza strona rysuje wypełniony okrąg o promieniu 50 wyśrodkowanym na stronie. Druga strona rysuje ramkę poza obszarem przycinania i dwie przekątne. Wreszcie ostatnia strona rysuje tekst wyśrodkowany na stronie. main () tworzy PrintSevice dla domyślnej drukarki, a dla tej drukarki tworzy zadanie drukowania. Następnie tworzony jest domyślny format strony, który jest formatem strony PORTRAIT z marginesem 1 cala. W następnym kroku tworzony jest obiekt Book, który tak naprawdę jest tylko zbiorem obiektów do druku, ale gdzie każdy obiekt ma format strony. Do tej kolekcji przypisane są trzy strony, gdzie pierwsza używa domyślnego obiektu PageFormat, podczas gdy druga strona używa PageFormat z marginesem 20 punktów, a na końcu ostatnia używa LANDSCAPE PageFormat. Po zainicjowaniu obiektu Book trzema stronami tworzony jest obiekt Doc

123 SimpleDoc doc = nowy SimpleDoc (książka, smak, null); że tym razem ma smak tego typu DocFlavor.SERVICE_FORMATTED.PAGEABLE a zatem dokument wielostronicowy DRUKUJ TEKST Zakończę ten rozdział drukiem przykładem, w którym programista formatuje poszczególne strony od zera. Jest rzadko potrzebny, ale odwrotnie, zapewnia całkowitą elastyczność w zakresie drukowania na drukarce. Program nazywa się HelloEgil. Zadanie polega na wydrukowaniu dokumentu ze stroną tytułową składającą się z nazwy i obrazu oraz tekstu składającego się z kilku linii, z których każda jest postrzegana jako akapit. Każdy wiersz zaczyna się od cyfry (1, 2 lub 3) i wskazuje typ akapitu, który jest używany w programie jako kod używanej czcionki. Program rozpoczyna ładowanie obrazu jako ImageIcon z ikoną nazwy, a tekst jako lista nazwana ArrayList <String>, gdzie każdy element jest linią (a zatem akapitem). Następnie wykonuje się następującą metodę: private void print() PrinterJob job = PrinterJob.getPrinterJob(); PageFormat format = job.defaultpage(); Book book = new Book(); book.append(new FrontPage(), format); pagination(book, format); job.setpageable(book); if (job.printdialog()) try job.print(); catch (PrinterException ex)

124 System.out.println(ex); który zaczyna się od utworzenia PrinterJob i domyślnego formatu strony dla tego zadania. W następnym kroku tworzony jest obiekt Book i dodawany jest obiekt FrontPage, który jest stroną na stronie tytułowej. FrontPage to wewnętrzna klasa reprezentująca obiekt do druku, który rysuje obraz skalowany i wyśrodkowany na stronie. Istnieje inna klasa wewnętrzna o nazwie TextPage, która reprezentuje tekst składający się z wielu linii, z których każda jest obiektem TextLayout. Ta klasa implementuje także do druku. Głównym zadaniem programu jest podzielenie dokumentu na obiekty TextPage, a tym samym strony tekstowe na drukarce. Dzieje się tak w następujący sposób: private void pagination(book book, PageFormat format) try TextPage page = new TextPage(); int width = (int)format.getimageablewidth(); int height = (int)format.getimageableheight(); int pos = 0; FontRenderContext frt = new FontRenderContext(null, false, false); for (String line : list) Font font = font12; if (line.length() > 0) if (line.charat(0) == '1') font = font36; else if (line.charat(0) == '2') font = font24; int lineheight = font.getsize(); AttributedString styledtext = new AttributedString(line.substring(1)); styledtext.addattribute(textattribute.font, font); AttributedCharacterIterator chariterator = styledtext.getiterator(); LineBreakMeasurer measurer = new LineBreakMeasurer(charIterator, frt); while (measurer.getposition() < chariterator.getendindex()) if (pos + lineheight < height) page.add(measurer.nextlayout(width)); else

125 book.append(page, format); pos = 0; page = new TextPage(); page.add(measurer.nextlayout(width)); pos += lineheight; if (pos + lineheight < height) page.add(new TextLayout(" ", font, frt)); pos += lineheight; else book.append(page, format); pos = 0; page = new TextPage(); else int lineheight = font.getsize(); if (pos + lineheight < height) page.add(new TextLayout(" ", font, frt)); pos += lineheight; else book.append(page, format); pos = 0; page = new TextPage(); if (page.getsize() > 0) book.append(page, format);

126 catch (Exception ex) System.out.println(ex.toString()); Nie muszę tutaj sprawdzać kodu, ale używa on tej samej techniki manipulacji tekstem, o której wspomniałem w poprzednim rozdziale tej książki, gdzie zadaniem programu jest zmierzenie, ile tekstu może znajdować się w poszczególnych wierszach, a także kojarzy czcionkę z tekstem. Następnie obiekt Book jest inicjowany obiektami do druku, a raport można wysłać do drukarki. 11 ZARZĄDZANIE PROGRAMAMI Z czasem większość programów musi być utrzymywana. Jest to jeden z powodów, dla których projekt programu i jakość programu odgrywają główną rolę, ponieważ programy o niezrozumiałym projekcie i trudnym do odczytania kodzie są prawie niemożliwe do utrzymania, a w najlepszym przypadku utrzymanie programu o złym projekcie może być czasochłonne.. Wiele programów żyje przez wiele lat, a okres ten wymaga regularnego utrzymywania programu. Jednym z powodów może być to, że program zawiera błędy, które oczywiście należy poprawić. Są to rzadko najtrudniejsze zadania konserwacyjne, ponieważ błędy są zwykle rozpoznawane wkrótce po uruchomieniu programów, a błędy powinny zazwyczaj być naprawiane przez te same osoby, które opracowały program, a zatem przez programistów, którzy mają przegląd i rozumieją kod programu. Innymi powodami konserwacji jest to, że program musi zostać rozszerzony o nowe funkcje lub istniejące funkcje powinny działać w inny sposób, a dla dużego programu, który będzie używany przez kilka lub wiele lat, z pewnością będą pojawiały się żądania takich zmian w miarę upływu czasu. Tutaj oryginalny projekt ma o wiele większe znaczenie dla zadania konserwacji, ponieważ zadanie to zwykle będzie wykonywane przez każdego innego niż deweloperzy, którzy pierwotnie opracowali program, i chociaż powinni to być ci sami ludzie, daleko im do tego, że pamiętam, jak napisano program. W rzeczywistości zadziwi nas, jak szybko zapomina się, jak napisano program. Wreszcie powodem utrzymania programu może być to, że po prostu chcesz go zmodernizować i wprowadzić na rynek jako nową wersję, co jest dość powszechne w przypadku różnych standardowych programów, takich jak pakiety biurowe i tak dalej. Jeśli istnieje zadanie konserwacji, ponieważ program wymaga rozszerzenia lub opracowania nowej wersji programu, zazwyczaj jest to nowy projekt programistyczny. Zasadniczo taki projekt jest podobny do innych projektów programistycznych, ale jeśli jest to program dobrej jakości z dobrym projektem, a jeśli program ma zostać rozszerzony lub część programu do modyfikacji, projekt można ograniczyć do włączenia co należy rozszerzyć lub zmienić, podczas gdy reszta programu może żyć bez zmian. Z drugiej strony, jeśli program został opracowany bez dalszego planowania, istnieje duże ryzyko, że zmiany mogą wpływać na skutki uboczne dużych części całego kodu, aw najgorszym przypadku nawet mniejsze zmiany mogą oznaczać, że duże części kodu należy przepisać. Na szczęście przy dobrym projekcie rzadko jest tak źle, ale zawsze istnieje powód, aby mieć świadomość, że zmiany w programie mogą łatwo powodować niezamierzone zmiany w kodzie gdzie indziej z błędami. W związku z tym należy zaplanować utrzymanie programów, aby upewnić się, że zidentyfikowałeś konsekwencje danych zmian i w pełni odkryłeś, co będzie później testowane, a jeśli to możliwe, wymagana jest dokumentacja, aby programiści, którzy wprowadzają zmiany, mogli szybko uzyskać przegląd architektury programu i sposobu jego wykonania. Rozpoczęcie każdego zadania konserwacyjnego polega zatem na przeczytaniu dokumentacji programu, a zwłaszcza dokumentu projektowego, i uzyskaniu przeglądu pełnego kodu, aby poznać znaczenie poszczególnych plików projektu. Rezultatem tego wszystkiego

127 jest to, że nie można jednoznacznie podkreślić znaczenia projektowania i architektury programu. W przeciwnym razie program nie może być utrzymany, a programy, które nie mogą być, są w zasadzie bezwartościowe. Niezależnie od tego, utrzymanie programów nie jest proste i podobnie jak tworzenie programów od uruchomienia, konserwacja rozpoczyna się również od analizy analizującej zmiany, które należy wprowadzić, i program, który należy utrzymać. Następnie zmodyfikuj / rozwiń istniejący projekt przed zakodowaniem zmian. W szczególności ważne jest, aby dokumentacja również została zaktualizowana, aby było jasne, co zostało zmienione, dlaczego, kiedy i wreszcie zmiany muszą zostać przetestowane, ze szczególnym uwzględnieniem faktu, że zmiany nie powodują błędów lub niedogodności w innym miejscu programu. PROBLEM 2 Zacznij od utworzenia kopii projektu kalendarza z książki Java 8. Poświęć trochę czasu na przetestowanie programu, aby mieć pewność, że pamiętasz, jak program działa. Teraz zadaniem jest dodanie do programu trzech funkcji drukowania: 1. Pasek narzędzi musi mieć dwie nowe ikony, a jeśli klikniesz tę, program powinien wydrukować kalendarz na bieżący miesiąc, a klikając na drugą ikonę, program powinien wydrukować kalendarz na bieżący rok. 2. Okno dialogowe uwag konserwacyjnych musi mieć dodatkowy przycisk, a kliknięcie przycisku pozwoli wydrukować notatkę. PROBLEM 3 Zacznij od utworzenia kopii projektu SlotMachine z książki Java 9. Poświęć trochę czasu na przetestowanie programu, aby mieć pewność, że pamiętasz, jak działa program. Zadanie polega na modyfikacji programu: 1. W istniejącej wersji nie ma animacji kół, ale animacja jest symulowana przez wyświetlanie poszczególnych figur na krótko przed wybraniem kolejnej figury. Teraz musisz zmienić program, więc zamiast tego symuluj obrót rolek za pomocą animacji w taki sam sposób, jak w rozdziale Program ma niefortunne administrowanie administratorem urządzenia, w którym można przejść do trybu administratora, klikając ikonę na pasku narzędzi. Należy to zmienić, aby określony użytkownik (gracz) miał prawo administratora. Jeśli ten użytkownik się zaloguje, ikony narzędzi administratora pojawią się automatycznie, a poza tym nie. Zadaniem tego jest upewnienie się, że zawsze istnieje administrator i że nie można go usunąć. 12 PACHART Przez bibliotekę wykresów zazwyczaj rozumiesz rodzinę klas, których możesz używać w programie do przedstawiania liczb za pomocą wykresów. Przykładami są histogramy, wykresy słupkowe, wykresy kołowe i inne. Dobrym przykładem użycia takich narzędzi jest arkusz kalkulacyjny, który oferuje różne formy graficznej reprezentacji liczb. Celem następującego projektu jest opracowanie takiej biblioteki, a także napisanie programu, który może zilustrować sposób korzystania z biblioteki. Istnieje wiele takich bibliotek i różnią się one między sobą, które wykresy są, jak ładne są wykresy, jakie opcje są możliwe, a także, co najważniejsze, jak łatwa w użyciu jest biblioteka. Celem projektu nie jest tak bardzo wartość biblioteki (jest tego bardzo dużo), ale jest to bardzo dobre ćwiczenie w korzystaniu z Java2D, szczególnie jeśli celem jest opracowanie ładnych grafów (cokolwiek może być), a jeśli skupisz się na tworzeniu biblioteki przyjaznej dla użytkownika. Jeśli chodzi o to drugie, należy pamiętać, że

128 łatwość obsługi nie jest tym samym, co najwyższa możliwa elastyczność, ale w większym stopniu, że biblioteka jest łatwa do zrozumienia i użytkowania. Wreszcie, niezwykle ważne jest, aby taka biblioteka była niezawodna i aby wykresy zachowywały się rozsądnie, jeśli są używane w oparciu o niekompletne lub nieodpowiednie dane, a tak naprawdę nie jest to takie łatwe. Poniżej nie skupię się na procesie, ale na tym, co jest zrobione i jak używana jest biblioteka BIBLIOTEKA Zasadniczo wykres powinien wizualizować liczby związane z kategoriami (etykietami). Klasycznym przykładem są liczby lat, z którymi każdy rok jest powiązany. Bieżąca biblioteka powinna zasadniczo oferować następujące wykresy: - Linia, na której wykres składa się z linii prostych między punktami - Histogram lub wykres słupkowy, który jest prawdopodobnie najczęściej stosowanym wykresem - Wykres kołowy lub kołowy Każdy z tych trzech podstawowych wykresów jest dostępny w kilku wariantach. Wykres liniowy ma dwa warianty - Krzywa, gdzie wykres jest zamiast tego rysowany jako miękka krzywa - Wykres, na którym wykres jest rysowany jako punkty - punkt dla każdej kategorii (etykiety), który można połączyć cienkimi liniami prostymi Wykres słupkowy jest rysowany domyślnie za pomocą pionowych pasków, ale istnieje wariant, w którym paski są rysowane poziomo, a na końcu istnieje odmiana zarówno pionowych, jak i poziomych wykresów słupkowych z prostym efektem 3D. Wreszcie istnieje wariant wykresu kołowego, w którym wykres jest rysowany jako prędkościomierz - tylko po to, aby zaznaczyć, że tylko wyobraźnia (i zdolność do rysowania ładnych wykresów) ogranicza wykresy, że taka biblioteka powinna zawierać. Biblioteka zawiera zatem 9 podstawowych (lub prostych) wykresów, które są zdefiniowane przez następujący typ: package pagraphs.charts; public enum GraphType LINE, CURVE, PLOT, VBAR, HBAR, VBAR3D, HBAR3D, PIE, SPEED Ponadto w przypadku wykresu wykresu można wskazać, która figura jest używana do przedstawienia punktów wykresu, a te opcje są zdefiniowane przez następujący typ, w którym nazwy wskazują, która figura jest używana: package pagraphs.charts; public enum PlotType CROSS, STAR, OPENCIRCLE, OPENSQUARE, OPENTRIANGLE, OPENRHOMBE, CLOSEDRHOMBE, CLOSEDCIRCLE, CLOSEDSQUARE, CLOSEDTRIANGLE Powyższy wykres nazywa się prostym (lub o pojedynczej wartości), co odpowiada faktowi, że jest on zasadniczo definiowany przez dwie tablice: 1. etykiety, które są tablicą ciągów znaków i reprezentują kategorie, a tym samym zmienną niezależną lub oś x

129 2. wartości, które są tablicą liczb o tej samej długości co etykiety i reprezentują zmienną zależną lub oś y Prosty wykres jest więc wykresem funkcji matematycznej, a oprócz dwóch tablic wykres dołącza również inne parametry, które wskazują, w jaki sposób należy narysować wykres. Wykres może być również wielowartościowy, co oznacza, że każda etykieta ma kilka liczb, a różnica polega na tym, że wartości tablic w zasadzie są tablicami dwuwymiarowymi. Wykres wielowartościowy jest domyślnie rysowany jako kilka prostych wykresów jeden za drugim (to znaczy jako seria wykresów). W szczególności wykres wielowartościowy może zostać scalony, co oznacza, że poszczególne wykresy są rysowane w tym samym układzie współrzędnych - w zakresie, w jakim ma to sens. To właśnie ta część sprawia, że rozwój poniższej biblioteki jest zarówno złożony, jak i kompleksowy. Zasadniczo wykres jest definiowany przy użyciu dwóch struktur danych. Pierwszy nazywa się SingleValues i reprezentuje wartości y prostego wykresu: package pagraphs.charts; import java.awt.*; public class SingleValues private String label; private String header; private GraphType type; private double begin; private double end; private int points; private int dec; private Paint color; private PlotType plot; private double param1; private double param2; private boolean onoff; private final double[] values; public SingleValues(GraphType type, String label, String header, double begin, double end, int points, int dec, Paint color, PlotType plot, double param1, double param2, boolean onoff, double values)

130 this.type = type; this.label = label; this.header = header; this.begin = begin; this.end = end; this.points = points; this.dec = dec; this.color = color; this.plot = plot; this.param1 = param1; this.param2 = param2; this.onoff = onoff; this.values = values; Krótko mówiąc, zmienne oznaczają: - label definiuje etykietę dla tej funkcji i jest typowo używana jako etykieta dla osi y, domyślnie jest pusta - header, jeśli nazwa lub opis tego wykresu, domyślnie jest puste - type jest rodzajem tego wykresu, domyślnie jest to VBAR - begin określa minimalną wartość na osi y, domyślnie 0 - end określa maksymalną wartość na osi y, domyślnie 0 i jeśli oba zaczynają się i end są 0 biblioteka automatycznie oblicza wartości til - points określa liczbę interwałów na osi y, domyślnie 0, a następnie biblioteka automatycznie oblicza wartość - dec określa liczbę miejsc po przecinku, które mają być użyte na osi y, domyślnie 0, a następnie biblioteka automatycznie oblicza wartość - color, który określa kolor wykresu - jeśli nie jest zdefiniowany, używany jest kolor domyślny - plot, który określa liczbę używaną dla wykresu wykresu (wartość jest używana tylko dla wykresu wykresu) - param 1, który jest specjalnym parametrem, którego aplikacja zależy od typu wykresu (jest używany dla wykresu LINIA, KRZYWA i PRĘDKOŚĆ), domyślnie 0 - param2, który jest specjalnym parametrem, którego aplikacja zależy od typu wykresu (jest używany dla wykresu SPEED), domyślnie 0 - onoff, który jest specjalnym parametrem, którego aplikacja zależy od typu wykresu (jest używany dla SROKI i wykresu WYKRESU), domyślnie jest to fałsz - value jest wartością tego wykresu Klasa ma niezbędne metody pobierania i ustawiania. Aby ułatwić definiowanie prostych wykresów, klasa ma więcej konstruktorów niż pokazano powyżej. Klasa SimpleValues definiuje tylko jedną zmienną zależną. Wykres, który może być jedno- lub wielowartościowy, definiuje się w następujący sposób: package pagraphs.charts;

131 import java.util.*; i import java.awt.color; public class MultiValues // defines a multi valued graph public static final Color colors[] = new Color(0x7C, 0xAF, 0xDD), new Color(0xF1, 0x9B, 0x5A), ; public final List<SingleValues> list; public final String labels[]; private String label; private boolean gitter; private boolean merge; private boolean same; private boolean stacked; private double scalex; private double scaley; private double minimum; private double maximum; public MultiValues(List<SingleValues> list, String label, boolean gitter, boolean merge, boolean same, boolean stacked, double scalex, double scaley, String labels) this.list = list; this.label = label; this.gitter = gitter; this.merge = merge; this.same = same; this.stacked = stacked;

132 this.scalex = scalex; this.scaley = scaley; his.labels = labels; Początkowo definiowana jest tablica domyślnych kolorów, która jest używana, jeśli nie zdefiniowano żadnych kolorów. W przeciwnym razie poszczególne właściwości oznaczają: - lista to lista obiektów typu SingleValues, która definiuje ten wykres - labels to tablica typu Ciąg z tekstami dla niezależnej osi (oś x lub oś etykiety) - label, która jest etykietą do osi x, domyślnie jest pusta - gitter określa, czy narysować linie siatki (prawda jest domyślna), czy nie (fałsz) - merge, który wskazuje, czy wykresy powinny zostać scalone w pojedynczy wykres (domyślnie false) - same, które określa, czy wszystkie wykresy (na wykresie scalania) muszą używać tej samej osi y (prawda jest domyślna) - właściwość jest używana tylko dla wykresu łączenia - stacked, które określa, czy wykresy BAR i BAR3D powinny być ułożone w stos (prawda), czy rysowane obok siebie (domyślnie fałsz) - właściwość jest używana tylko dla wykresu scalania - scalex, który określa skalowanie w kierunku osi x (poziomej) (1 jest domyślny, a oznacza brak skalowania) - skala Y, która określa skalowanie w kierunku osi Y (pionowej) (1 jest domyślna i oznacza brak skalowania) - minimum, które jest używane dla wykresu scalania do obliczenia minimalnej wartości dla osi y osi - maximum, które jest używane dla wykresu scalania do obliczenia maksymalnej wartości dla osi y osi Klasa ma niezbędne metody pobierania i ustawiania. Aby ułatwić definiowanie wykresów, klasa ma więcej konstruktorów niż pokazano powyżej. Jak wspomniano, istnieje 9 różnych wykresów, a także możliwość scalenia wielu wykresów w tym samym układzie współrzędnych. Wykresy są rysowane za pomocą klas szuflad, których zadaniem jest rysowanie określonego wykresu, a także przechwytywanie kliknięć myszą. Jeśli klikniesz wykres za pomocą myszy, musi on wywołać zdarzenie: 1. po kliknięciu etykiety (na osi x) nazwa jest powiązana ze zdarzeniem 2. po kliknięciu na sam wykres zdarzenie skojarzyło zarówno nazwę na etykiecie osi x, jak i odpowiednią wartość funkcji Zdarzenie ma typ ChartEvent, który ma argument następującego typu: package pagraphs.charts; public class FunctionValue

133 private final String name; private final Double value; public FunctionValue(String name, Double value) this.name = name; this.value = value; public boolean hasvalue() return value!= null; public String getname() return name; public Double getvalue() return value; Tutaj jest wartość null, jeśli klikniesz etykietę. Powodem jest to, że wykres może być wielowartościowy, a zatem do określonej etykiety może być przypisanych kilka wartości. Obiekt nasłuchiwania dla zdarzeń tego typu zaimplementuje interfejs ChartListener, który definiuje jedną procedurę obsługi zdarzeń. Wykres jest składnikiem niestandardowym: public class ChartComponent extends JComponent implements MouseListener private ArrayList<ChartListener> listeners = new ArrayList(); private MultiValues data; private Color bordercolor;

134 private int gap; private java.util.list<drawer> drawers; public ChartComponent(MultiValues data, Font font, int gap, Color bordercolor) this.data = data; this.gap = gap; this.bordercolor = bordercolor; setfont(font); drawers = DrawerFactory.createDrawer(data, getfont()); addmouselistener(this); setbackground(color.white); public Dimension getpreferredsize() double width = 0; double height = 0; for (int n = 0; n < drawers.size(); ++n) width += drawers.get(n).getwidth(n) + gap; double h = drawers.get(n).getheight(n); if (height < h) height = h; return new Dimension((int)width, (int)height); public void paintcomponent(graphics g) Graphics2D g2d = (Graphics2D)g; Dimension size = getsize(); g2d.setpaint(getbackground()); g2d.fill(new Rectangle2D.Double(0, 0, size.width, size.height)); g2d.setfont(getfont()); DrawingTools.smootGraphics(g2d); double offset = 0;

135 if (data.ismerge()) offset = ((MergeDrawer)drawers.get(0)).draw(g2d, 0, 0, offset); else for (int n = 0; n < data.list.size(); ++n) offset += ((SingleDrawer)drawers.get(n)).draw(g2d, n, 0, 0, offset) + gap; if (bordercolor!= null) g2d.setstroke(new BasicStroke(0.5f)); g2d.setpaint(bordercolor); Dimension dim = getpreferredsize(); g2d.setclip(new Rectangle2D.Double(0, 0, dim.width + 1, dim.height + 1)); g2d.draw(new Rectangle2D.Double(0, 0, dim.width, dim.height)); public void addchartlistener(chartlistener listener) listeners.add(listener); public void removechartlistener(chartlistener listener) listeners.remove(listener); public void mouseclicked(mouseevent e) for (int n = 0; n < drawers.size(); ++n) FunctionValue functionvalue = drawers.get(n).getclicked(n, e.getx(), e.gety()); if (functionvalue!= null) ChartEvent event = new ChartEvent(this, functionvalue); for (ChartListener listener : listeners) listener.chartclicked(event); return;

136 Składnik jest tworzony na podstawie obiektu MultiValues, a zatem definicji wykresu. Ponadto należy określić czcionkę, która ma zostać użyta, a także określić odstęp między poszczególnymi wykresami (jeśli jest to wykres wielowartościowy) i jeśli wokół wykresu należy narysować krawędź. Klasa ma więcej konstruktorów niż pokazano powyżej. Korzystając z definicji wykresu, konstruktor tworzy listę obiektów szuflady, w których dla każdego wykresu znajduje się szuflada. Te obiekty Drawer są tworzone za pomocą klasy DrawerFactory, która jest prostą klasą fabryczną. Metoda paintcomponent () używa tych obiektów Drawer do rysowania komponentu, a także paintcomponent () rysuje krawędź wokół komponentu. W przypadku narysowania krawędzi używany jest preferowany rozmiar komponentu, który jest również określany za pomocą obiektów Szuflady. To samo dotyczy myszy, aby ustalić, czy kliknięto wykres. Oznacza to, że klasy szuflad są absolutnie kluczowymi klasami: Szuflada to interfejs definiujący trzy metody określające szerokość i wysokość n-tego wykresu w ChartComponent, a także metodę, która zwraca obiekt FunctionValue po kliknięciu tej szuflady. Ogólnie wykres jest rysowany jako prostokątna figura z marginesem. Margines jest używany, na przykład, do osi układu współrzędnych, do tekstu nagłówka i do tego, do czego służy margines rysunku, a zatem rozmiar marginesu jest określony przez bieżący wykres (nie wszystkie wykresy mają układ współrzędnych). SingleDrawer to klasa abstrakcyjna, która implementuje funkcje interfejsu, a także metody określające margines wykresu i rysujące go. Klasa definiuje również funkcje skalowania i tutaj należy pamiętać, że skalują one tylko wykres, ale nie margines. Jako przykład pokazano dwa wykresy, z tą różnicą, że skaluje się drugi:

137 Typ powyższego wykresu to VBAR, i istnieje konkretna klasa VbarDrawer, pochodząca z SingleDrawer. To ta szuflada służy do rysowania powyższych wykresów. Istnieje odpowiednio 8 innych specyficznych klas Drawer, które wywodzą się z SingleDrawer. Jako kolejny przykład pokazano poniżej wykres wielowartościowy, gdzie pierwszy ma typ HBAR i jest rysowany za pomocą HbarDrawer, podczas gdy drugi jest typu PIE i jest rysowany za pomocą PieDrawer: Kod tych 9 konkretnych klas szuflad jest do siebie podobny i zasadniczo są one dość proste, chociaż oczywiście może być wiele szczegółów związanych z implementacją funkcji rysowania. Klasy używają więcej klas pomocniczych, w tym klas rysujących osie. Tutaj szczególnie klasy, które rysują osie y, są złożone, ponieważ zazwyczaj zależą od liczb rzeczywistych, aby dokonać rozsądnego podziału osi. Jest też klasa MergeDrawer, która jest również klasą konkretną, ale rysuje wykres przy użyciu innych obiektów Drawer. Jeśli masz funkcję wielowartościową, możesz rysować wykresy w tym samym układzie współrzędnych, co nazywam grafem scalania. Jako przykład pokazano połączenie wykresu LINE i wykresu VBAR:

138 Teraz ogólnie nie można scalać wszystkich wykresów. Na przykład nie ma sensu scalanie histogramu (wykresu VBAR) i wykresu pia i nie ma sensu scalanie wykresu VBAR i wykresu HBAR. Klasa MergeDrawer zaczyna od podzielenia wykresów na niektóre kategorie podobne do tego, w jaki sposób można łączyć, a dla każdej z tych kategorii definiowana jest klasa szuflady. Prowadzi to do nie mniej niż 14 nowych klas szuflad, z których wszystkie są zasadniczo podobne do powyższych, ale zamiast tego pochodzą od abstrakcyjnej klasy MultiDrawer, która jako pojedyncza szuflada tym razem zawsze rysuje wykres wielowartościowy. Gdy jest tak wiele klas MultiDrawer z powodu 1. że możesz określić, czy połączone wykresy powinny używać tej samej osi Y, czy każde z nich ma używać własnej osi Y. 2. że możesz określić, czy wykresy słupkowe mają być rysowane obok siebie, czy układane w stosy. W szczególności odpowiada to następującym klasom MultiDrawer: 1. MSLCPBDrawer, który łączy wykresy LINE, CURVE, PLOT i VBAR z tą samą osią y 2. MDLCPBDrawer, który łączy wykresy LINE, CURVE, PLOT i VBAR z różnymi osiami y 3. MSVBAR3DDrawer, który łączy wykresy VBAR3D z tą samą osią y 4. MDVBAR3DDrawer, który łączy wykresy VBAR3D z inną osią y 5. MSHBARDrawer, który łączy wykresy HBAR z tą samą osią y 6. MDHBARDrawer, który łączy wykresy HBAR z różnymi osiami y 7. MSHBAR3DDrawer, który łączy wykresy HBAR3D z tą samą osią y 8. MDHBAR3DDrawer, który łączy wykresy HBAR3D z różnymi osiami y 9. MSLCPDrawer, który łączy wykresy LINIA, KRZYWA i WYKRES z tą samą osią y, gdy wybrany jest stos wykresów słupkowych 10. MSLCPDrawer, który łączy wykresy LINIA, KRZYWA i WYKRES z inną osią y, gdy wybrany jest stos wykresów słupkowych

139 11. SVBARDrawer, który układa grafy VBAR 12. SHBARDrawer, który układa wykresy HBAR 13. SVBAR3DDrawer, który układa grafy VBAR3D 14. SHBAR3DDrawer, który układa wykresy HBAR3D Jeśli wykres wielowartościowy jest scalony i istnieje wykres, którego nie można scalić z innymi, jest on rysowany jako wykres SingleValues przy użyciu odpowiedniej szuflady. Wynikiem tego wszystkiego jest to, że istnieje wiele kodów związanych ze scalaniem wykresów, ale praca polega przede wszystkim na napisaniu odpowiednich klas Drawer, z których wszystkie wyglądają podobnie PROGRAM TESTOWY Następnie jest program testowy, który otwiera okno z tabelą JT zawierającą liczby - w pobliżu pierwszej kolumny, która zawiera teksty, które mają być używane jako etykiety dla wykresów. U góry znajduje się pasek narzędzi z przyciskami. Kliknięcie pierwszego przycisku pozwala określić przedział dla liczb w tabeli, a program wypełni tabelę losowymi liczbami z tego zakresu. Następnie pojawia się 19 przycisków testowych, których otwartych okien używałem do testowania w związku z rozwojem biblioteki. Najciekawszy jest przycisk po prawej stronie. Jeśli wybierzesz prostokątny obszar w tabeli (który nie zawiera lewej kolumny) i klikniesz przycisk, program utworzy obiekt MultiValues, w którym możesz edytować właściwości:

140 W tym miejscu wybrano trzy kolumny i zdefiniowano wykres wielo-graficzny dla trzech wykresów. Dwukrotne kliknięcie nagłówka kolumny dla jednego z trzech wykresów powoduje wyświetlenie następującego okna dialogowego, w którym można zdefiniować ustawienia dla poszczególnych wykresów: W ten sposób możesz użyć programu do zdefiniowania różnych ustawień dla poszczególnych wykresów i przetestowania efektu. Należy pamiętać, że w celu praktycznego wykorzystania ostatnie okno dialogowe musi być dostosowane do poszczególnych typów wykresów, dlatego można wprowadzać i wybierać tylko wartości właściwości, które mają sens dla bieżącego wykresu BRAKI I RZECZY, KTÓRE MOGĄ BYĆ POPRAWIONE Wykresy nie mogą wyświetlać wszystkich liczb z rozsądnym wynikiem, a niektóre wykresy, na przykład, są mylone z liczbami ujemnymi, szczególnie jeśli scalasz wykresy. Na przykład nie ma sensu układać wykresów słupkowych, jeśli liczby mogą być ujemne. Wniosek jest taki, że kilka wykresów ma warunki wstępne, które funkcje rysowania z korzyścią mogłyby zweryfikować i ewentualnie podnieść wykonanie, jeśli wykres nie może zostać poprawnie narysowany. Alternatywą może być pozwolenie, aby klasy Drawer zwróciły 0, jeśli wykresu nie da się narysować z wystarczająco dobrym wynikiem - strategia, która jest już częściowo wdrożona. Rzeczywisty składnik ChartComponent może wywołać pojedyncze zdarzenie, jeśli użytkownik kliknie wykres. Składnik jest rysowany na podstawie obiektu typu MultiValues, a składnik może zostać rozszerzony, ponieważ uruchamia PropertyChangeEvent, jeśli wartość właściwości zostanie zmieniona w strukturze danych MultiValues. W praktyce oznaczałoby to zwykle przerysowanie komponentu, a także może mieć znaczenie dla menedżera układu, który zawiera

narzędzie Linia. 2. W polu koloru kliknij kolor, którego chcesz użyć. 3. Aby coś narysować, przeciągnij wskaźnikiem w obszarze rysowania.

narzędzie Linia. 2. W polu koloru kliknij kolor, którego chcesz użyć. 3. Aby coś narysować, przeciągnij wskaźnikiem w obszarze rysowania. Elementy programu Paint Aby otworzyć program Paint, należy kliknąć przycisk Start i Paint., Wszystkie programy, Akcesoria Po uruchomieniu programu Paint jest wyświetlane okno, które jest w większej części

Bardziej szczegółowo

Język Java. Rysowanie GUI Określanie wyglądu komponentów

Język Java. Rysowanie GUI Określanie wyglądu komponentów Język Java Rysowanie GUI Określanie wyglądu komponentów Rysowanie GUI Rysowanie GUI w Swingu np. przy pierwszym wyświetleniu przy ponownym odsłonięciu przy zmianach stanu programu Kolejność rysowania -

Bardziej szczegółowo

Java. Wykład 9. Piotr Tronczyk

Java. Wykład 9. Piotr Tronczyk Java Wykład 9 Piotr Tronczyk Zegar analogowy Tarcza Cyferblat Wskazówki Timer 2 Zegar analogowy Tym razem postaramy się napisać program, który wyświetlał będzie zegar analogowy. Część odpowiedzialna za

Bardziej szczegółowo

4. W konstruktorze klasy Grafika wywołaj metodę określającą rozmiary ramki oraz ustaw kolor tła metodą setbackground():

4. W konstruktorze klasy Grafika wywołaj metodę określającą rozmiary ramki oraz ustaw kolor tła metodą setbackground(): INSTRUKCJA DO ĆWICZENIA 2 Aplikacja zawiera przykłady ilustrujące wybrane metody graficzne klasy Graphics (powtórzenie) oraz klasy Graphics2D. Tworzenie własnej klasy, tworzącej wieloboki o zadanym kształcie

Bardziej szczegółowo

Wizualne systemy programowania. Wykład 11 Grafika. dr Artur Bartoszewski -Wizualne systemy programowania, sem. III- WYKŁAD

Wizualne systemy programowania. Wykład 11 Grafika. dr Artur Bartoszewski -Wizualne systemy programowania, sem. III- WYKŁAD Wizualne systemy programowania Wykład 11 Grafika 1 dr Artur Bartoszewski -Wizualne systemy programowania, sem. III- WYKŁAD Grafika GDI+ GDI+ - Graphics Device Interface jeden z trzech podstawowych komponentów

Bardziej szczegółowo

4. Rysowanie krzywych

4. Rysowanie krzywych 1. Operator plot y x \begin{tikzpicture} \draw[->] (-0.2,0) -- (4.2,0) node[right] {$x$}; \draw[->] (0,-1.2) -- (0,4.2) node[above] {$y$}; \draw (3,4) -- (3,3) plot coordinates{(2,3) (3,0) (4,3)}; \end{tikzpicture}

Bardziej szczegółowo

Obsługa programu Paint materiały szkoleniowe

Obsługa programu Paint materiały szkoleniowe Obsługa programu Paint materiały szkoleniowe Nota Materiał powstał w ramach realizacji projektu e-kompetencje bez barier dofinansowanego z Programu Operacyjnego Polska Cyfrowa działanie 3.1 Działania szkoleniowe

Bardziej szczegółowo

Programowanie Multimediów. Programowanie Multimediów JAVA. grafika w JAVA 2D API [1]

Programowanie Multimediów. Programowanie Multimediów JAVA. grafika w JAVA 2D API [1] JAVA grafika w JAVA 2D API [1] Wprowadzenie Java2D API w sposób znaczny rozszerza możliwości graficzne AWT. Po pierwsze umożliwia zarządzanie i rysowanie elementów graficznych o współrzędnych zmiennoprzecinkowych

Bardziej szczegółowo

Grażyna Koba. Grafika komputerowa. materiały dodatkowe do podręcznika. Informatyka dla gimnazjum

Grażyna Koba. Grafika komputerowa. materiały dodatkowe do podręcznika. Informatyka dla gimnazjum Grażyna Koba Grafika komputerowa materiały dodatkowe do podręcznika Informatyka dla gimnazjum Rysunki i animacje w Edytorze postaci 1. Rysunek w Edytorze postaci Edytor postaci (rys. 1.) jest częścią programu

Bardziej szczegółowo

Projekt współfinansowany ze środków Unii Europejskiej w ramach Europejskiego Funduszu Społecznego

Projekt współfinansowany ze środków Unii Europejskiej w ramach Europejskiego Funduszu Społecznego Projekt graficzny z metamorfozą (ćwiczenie dla grup I i II modułowych) Otwórz nowy rysunek. Ustal rozmiar arkusza na A4. Z przybornika wybierz rysowanie elipsy (1). Narysuj okrąg i nadaj mu średnicę 100

Bardziej szczegółowo

Grafika i komunikacja człowiek komputer Laboratorium. Część 2: Graphics

Grafika i komunikacja człowiek komputer Laboratorium. Część 2: Graphics UNIWERSYTET RZESZOWSKI KATEDRA INFORMATYKI Opracował: mgr inż. Przemysław Pardel, dr hab. Bogdan Kwolek v1.01 2010 Grafika i komunikacja człowiek komputer Laboratorium Część 2: Graphics ZAGADNIENIA DO

Bardziej szczegółowo

Wprowadzenie do rysowania w 3D. Praca w środowisku 3D

Wprowadzenie do rysowania w 3D. Praca w środowisku 3D Wprowadzenie do rysowania w 3D 13 Praca w środowisku 3D Pierwszym krokiem niezbędnym do rozpoczęcia pracy w środowisku 3D programu AutoCad 2010 jest wybór odpowiedniego obszaru roboczego. Można tego dokonać

Bardziej szczegółowo

CorelDraw - podstawowe operacje na obiektach graficznych

CorelDraw - podstawowe operacje na obiektach graficznych CorelDraw - podstawowe operacje na obiektach graficznych Przesuwanie obiektu Wymaż obszar roboczy programu CorelDraw (klawisze Ctrl+A i Delete). U góry kartki narysuj dowolnego bazgrołka po czym naciśnij

Bardziej szczegółowo

Java: otwórz okienko. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

Java: otwórz okienko. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak Java: otwórz okienko Programowanie w językach wysokiego poziomu mgr inż. Anna Wawszczak PLAN WYKŁADU klasy wewnętrzne, lokalne i anonimowe biblioteka AWT zestaw Swing JFrame JPanel komponenty obsługa zdarzeń

Bardziej szczegółowo

Microsoft Small Basic

Microsoft Small Basic Microsoft Small Basic Okno grafiki Szacowany czas trwania lekcji: 1 godzina Okno grafiki Podczas tej lekcji uzyskasz informacje na temat: Instrukcji używających obiektu GraphicsWindow. Właściwości obiektu

Bardziej szczegółowo

Narysujemy uszczelkę podobną do pokazanej na poniższym rysunku. Rys. 1

Narysujemy uszczelkę podobną do pokazanej na poniższym rysunku. Rys. 1 Narysujemy uszczelkę podobną do pokazanej na poniższym rysunku. Rys. 1 Jak zwykle, podczas otwierania nowego projektu, zaczynamy od ustawienia warstw. Poniższy rysunek pokazuje kolejne kroki potrzebne

Bardziej szczegółowo

Opis implementacji: Poznanie zasad tworzenia programów komputerowych za pomocą instrukcji języka programowania.

Opis implementacji: Poznanie zasad tworzenia programów komputerowych za pomocą instrukcji języka programowania. Nazwa implementacji: Robot biedronka Autor: Jarosław Żok Opis implementacji: Poznanie zasad tworzenia programów komputerowych za pomocą instrukcji języka programowania. Gra została zaimplementowana z wykorzystaniem

Bardziej szczegółowo

KONSTRUKCJA TRÓJKĄTA 1 KONSTRUKCJA TRÓJKĄTA 2 KONSTRUKCJA CZWOROKĄTA KONSTRUKCJA OKRĘGU KONSTRUKCJA STYCZNYCH

KONSTRUKCJA TRÓJKĄTA 1 KONSTRUKCJA TRÓJKĄTA 2 KONSTRUKCJA CZWOROKĄTA KONSTRUKCJA OKRĘGU KONSTRUKCJA STYCZNYCH Wstęp Ten multimedialny program edukacyjny zawiera zadania konstrukcyjne pozwalające na samodzielne ćwiczenie i sprawdzenie wiadomości w zakresie konstrukcji podstawowych figur geometrycznych. Jest przeznaczony

Bardziej szczegółowo

CorelDRAW. wprowadzenie

CorelDRAW. wprowadzenie CorelDRAW wprowadzenie Źródło: Podręcznik uŝytkownika pakietu CorelDRAW Graphics Suite 12 Rysowanie linii 1. Otwórz program CorelDRAW. 2. Utwórz nowy rysunek i zapisz go w swoich dokumentach jako [nazwisko]_1.cdr

Bardziej szczegółowo

Ćwiczenie 1 Automatyczna animacja ruchu

Ćwiczenie 1 Automatyczna animacja ruchu Automatyczna animacja ruchu Celem ćwiczenia jest poznanie procesu tworzenia automatycznej animacji ruchu, która jest podstawą większości projektów we Flashu. Ze względu na swoją wszechstronność omawiana

Bardziej szczegółowo

Maskowanie i selekcja

Maskowanie i selekcja Maskowanie i selekcja Maska prostokątna Grafika bitmapowa - Corel PHOTO-PAINT Pozwala definiować prostokątne obszary edytowalne. Kiedy chcemy wykonać operacje nie na całym obrazku, lecz na jego części,

Bardziej szczegółowo

CorelDraw - wbudowane obiekty wektorowe - prostokąty Rysowanie prostokątów

CorelDraw - wbudowane obiekty wektorowe - prostokąty Rysowanie prostokątów CorelDraw - wbudowane obiekty wektorowe - prostokąty Rysowanie prostokątów Naciskamy klawisz F6 lub klikamy w ikonę prostokąta w przyborniku po lewej stronie ekranu - zostanie wybrane narzędzie prostokąt.

Bardziej szczegółowo

Ćwiczenie 6 Animacja trójwymiarowa

Ćwiczenie 6 Animacja trójwymiarowa Animacja trójwymiarowa Wstęp Jedną z nowości Flasha CS4 i wyższych wersji jest tworzenie animacji 3D. Są do tego przeznaczone narzędzia Obrót 3D (W) i Translacja 3D (G). Narzędzia te działają na klipach

Bardziej szczegółowo

Kurs Adobe Photoshop Elements 11

Kurs Adobe Photoshop Elements 11 Kurs Adobe Photoshop Elements 11 Gladiatorx1 Kształty, kształty własne 2015-01- 01 Spis treści Wstęp... 2 Kształty... 2 Opcje narzędzia... 2 Rysujemy kształty... 5 Opcje dodawania, odejmowania obszaru

Bardziej szczegółowo

Politechnika Warszawska Wydział Mechatroniki Instytut Automatyki i Robotyki

Politechnika Warszawska Wydział Mechatroniki Instytut Automatyki i Robotyki Politechnika Warszawska Wydział Mechatroniki Instytut Automatyki i Robotyki Ćwiczenie laboratoryjne 2 Temat: Modelowanie powierzchni swobodnych 3D przy użyciu programu Autodesk Inventor Spis treści 1.

Bardziej szczegółowo

Rys. 1. Rozpoczynamy rysunek pojedynczej części

Rys. 1. Rozpoczynamy rysunek pojedynczej części Inventor cw1 Otwieramy nowy rysunek typu Inventor Part (ipt) pojedyncza część. Wykonujemy to następującym algorytmem, rys. 1: 1. Na wstędze Rozpocznij klikamy nowy 2. W oknie dialogowym Nowy plik klikamy

Bardziej szczegółowo

Rysowanie punktów na powierzchni graficznej

Rysowanie punktów na powierzchni graficznej Rysowanie punktów na powierzchni graficznej Tworzenie biblioteki rozpoczniemy od podstawowej funkcji graficznej gfxplot() - rysowania pojedynczego punktu na zadanych współrzędnych i o zadanym kolorze RGB.

Bardziej szczegółowo

1. OPEN OFFICE RYSUNKI

1. OPEN OFFICE RYSUNKI 1. 1 1. OPEN OFFICE RYSUNKI 1.1 Wiadomości podstawowe Po uruchomieniu programu Draw okno aplikacji wygląda jak na poniższym rysunku. Składa się ono z głównego okna, w którym edytuje się rysunek oraz czterech

Bardziej szczegółowo

KGGiBM GRAFIKA INŻYNIERSKA Rok III, sem. VI, sem IV SN WILiŚ Rok akademicki 2011/2012

KGGiBM GRAFIKA INŻYNIERSKA Rok III, sem. VI, sem IV SN WILiŚ Rok akademicki 2011/2012 Rysowanie precyzyjne 7 W ćwiczeniu tym pokazane zostaną wybrane techniki bardzo dokładnego rysowania obiektów w programie AutoCAD 2012, między innymi wykorzystanie punktów charakterystycznych. Narysować

Bardziej szczegółowo

Grafika i komunikacja człowiek komputer Laboratorium. Część 1: Wstęp do grafiki

Grafika i komunikacja człowiek komputer Laboratorium. Część 1: Wstęp do grafiki UNIWERSYTET RZESZOWSKI KATEDRA INFORMATYKI Opracował: mgr inż. Przemysław Pardel, dr hab. Bogdan Kwolek v1.01 2010 Grafika i komunikacja człowiek komputer Laboratorium Część 1: Wstęp do grafiki ZAGADNIENIA

Bardziej szczegółowo

Java 2D. dr Jarosław Skaruz http://jareks.ii.uph.edu.pl jaroslaw@skaruz.com

Java 2D. dr Jarosław Skaruz http://jareks.ii.uph.edu.pl jaroslaw@skaruz.com Java 2D dr Jarosław Skaruz http://jareks.ii.uph.edu.pl jaroslaw@skaruz.com Wprowadzenie Java 2D API umożliwia pracę z obrazami, tekstem i grafiką jako rozszerzenie AWT Zakres dostarczanej funkcjonalności:

Bardziej szczegółowo

Cykl lekcji informatyki w klasie IV szkoły podstawowej. Wstęp

Cykl lekcji informatyki w klasie IV szkoły podstawowej. Wstęp Cykl lekcji informatyki w klasie IV szkoły podstawowej Wstęp Poniżej przedstawiam cykl początkowych lekcji informatyki poświęconym programowi Paint. Nie są to scenariusze lekcji, lecz coś w rodzaju kart

Bardziej szczegółowo

Obsługa mapy przy użyciu narzędzi nawigacji

Obsługa mapy przy użyciu narzędzi nawigacji Obsługa mapy przy użyciu narzędzi nawigacji Narzędzia do nawigacji znajdują się w lewym górnym rogu okna mapy. Przesuń w górę, dół, w lewo, w prawo- strzałki kierunkowe pozwalają przesuwać mapę w wybranym

Bardziej szczegółowo

CorelDRAW. 1. Rysunek rastrowy a wektorowy. 2. Opis okna programu

CorelDRAW. 1. Rysunek rastrowy a wektorowy. 2. Opis okna programu 1. Rysunek rastrowy a wektorowy CorelDRAW Różnice między rysunkiem rastrowym (czasami nazywanym bitmapą) a wektorowym są olbrzymie. Szczególnie widoczne są podczas skalowania (czyli zmiany rozmiaru) rysunku

Bardziej szczegółowo

- biegunowy(kołowy) - kursor wykonuje skok w kierunku tymczasowych linii konstrukcyjnych;

- biegunowy(kołowy) - kursor wykonuje skok w kierunku tymczasowych linii konstrukcyjnych; Ćwiczenie 2 I. Rysowanie precyzyjne Podczas tworzenia rysunków często jest potrzeba wskazania dokładnego punktu na rysunku. Program AutoCad proponuje nam wiele sposobów zwiększenia precyzji rysowania.

Bardziej szczegółowo

Ćwiczenie 3: Rysowanie obiektów w programie AutoCAD 2010

Ćwiczenie 3: Rysowanie obiektów w programie AutoCAD 2010 Ćwiczenie 3: Rysowanie obiektów w programie AutoCAD 2010 1 Przeznaczone dla: nowych użytkowników programu AutoCAD Wymagania wstępne: brak Czas wymagany do wykonania: 15 minut W tym ćwiczeniu Lekcje zawarte

Bardziej szczegółowo

Podstawy Processingu. Diana Domańska. Uniwersytet Śląski

Podstawy Processingu. Diana Domańska. Uniwersytet Śląski Podstawy Processingu Diana Domańska Uniwersytet Śląski Processing jest językiem programowania opartym na języku Java. Jest on nastawiony na aplikacje związane z grafiką, animacją. Projekt został zainicjowany

Bardziej szczegółowo

Rysowanie precyzyjne. Polecenie:

Rysowanie precyzyjne. Polecenie: 7 Rysowanie precyzyjne W ćwiczeniu tym pokazane zostaną różne techniki bardzo dokładnego rysowania obiektów w programie AutoCAD 2010, między innymi wykorzystanie punktów charakterystycznych. Z uwagi na

Bardziej szczegółowo

Ćw. I Projektowanie opakowań transportowych cz. 1 Ćwiczenia z Corel DRAW

Ćw. I Projektowanie opakowań transportowych cz. 1 Ćwiczenia z Corel DRAW Ćw. I Projektowanie opakowań transportowych cz. 1 Ćwiczenia z Corel DRAW Celem ćwiczenia jest wstępne przygotowanie do wykonania projektu opakowania transportowego poprzez zapoznanie się z programem Corel

Bardziej szczegółowo

Skalowanie i ustawianie arkuszy/układów wydruku w AutoCAD autor: M. Motylewicz, 2012

Skalowanie i ustawianie arkuszy/układów wydruku w AutoCAD autor: M. Motylewicz, 2012 1 z 72 Rysunek rysujemy w skali rzeczywistej tzn. jeżeli pas ruchu ma szerokość 3,5m to wpisujemy w AutoCAD: 3,5 jednostki (mapa oczywiście również musi być wstawiona w skali 1:1). Opisany w dalszym ciągu

Bardziej szczegółowo

Tworzenie elementów graficznych

Tworzenie elementów graficznych Tworzenie elementów graficznych Elementy graficzne w Javie pozwalające tworzyć Graficzny Interfejs Użytkownika (GUI) możemy podzielić na dwie grupy: AWT (Abstract Window Toolkit) bibliotek klas służąca

Bardziej szczegółowo

Ćwiczenie nr 1. Kliknij myszką w trójkąt, aby otrzymać dostęp do uchwytów obrotów:

Ćwiczenie nr 1. Kliknij myszką w trójkąt, aby otrzymać dostęp do uchwytów obrotów: Ćwiczenie nr 1 Wybierz narzędzie wielokąt, ustaw na pasku własności liczbę boków równą 3 i z pomocą klawisza Ctrl narysuj trójkąt równoboczny, po czym naciśnij spację, aby przełączyć się na wskaźnik: Kliknij

Bardziej szczegółowo

Tworzenie dokumentacji 2D

Tworzenie dokumentacji 2D Tworzenie dokumentacji 2D Tworzenie dokumentacji technicznej 2D dotyczy określonej części (detalu), uprzednio wykonanej w przestrzeni trójwymiarowej. Tworzenie rysunku 2D rozpoczynamy wybierając z menu

Bardziej szczegółowo

Obsługa programu Paint. mgr Katarzyna Paliwoda

Obsługa programu Paint. mgr Katarzyna Paliwoda Obsługa programu Paint. mgr Katarzyna Paliwoda Podstawowo program mieści się w Systemie a dojście do niego odbywa się przez polecenia: Start- Wszystkie programy - Akcesoria - Paint. Program otwiera się

Bardziej szczegółowo

RYSUNEK TECHNICZNY I GEOMETRIA WYKREŚLNA INSTRUKCJA DOM Z DRABINĄ I KOMINEM W 2D

RYSUNEK TECHNICZNY I GEOMETRIA WYKREŚLNA INSTRUKCJA DOM Z DRABINĄ I KOMINEM W 2D Politechnika Białostocka Wydział Budownictwa i Inżynierii Środowiska Zakład Informacji Przestrzennej Inżynieria Środowiska INSTRUKCJA KOMPUTEROWA z Rysunku technicznego i geometrii wykreślnej RYSUNEK TECHNICZNY

Bardziej szczegółowo

Adobe InDesign lab.1 Jacek Wiślicki, Paweł Kośla. Spis treści: 1 Podstawy pracy z aplikacją Układ strony... 2.

Adobe InDesign lab.1 Jacek Wiślicki, Paweł Kośla. Spis treści: 1 Podstawy pracy z aplikacją Układ strony... 2. Spis treści: 1 Podstawy pracy z aplikacją... 2 1.1 Układ strony... 2 strona 1 z 7 1 Podstawy pracy z aplikacją InDesign jest następcą starzejącego się PageMakera. Pod wieloma względami jest do niego bardzo

Bardziej szczegółowo

Baltie 3. Podręcznik do nauki programowania dla klas I III gimnazjum. Tadeusz Sołtys, Bohumír Soukup

Baltie 3. Podręcznik do nauki programowania dla klas I III gimnazjum. Tadeusz Sołtys, Bohumír Soukup Baltie 3 Podręcznik do nauki programowania dla klas I III gimnazjum Tadeusz Sołtys, Bohumír Soukup Czytanie klawisza lub przycisku myszy Czytaj klawisz lub przycisk myszy - czekaj na naciśnięcie Polecenie

Bardziej szczegółowo

Jarosław Kuchta Podstawy Programowania Obiektowego. Podstawy grafiki obiektowej

Jarosław Kuchta Podstawy Programowania Obiektowego. Podstawy grafiki obiektowej Jarosław Kuchta Podstawy Programowania Obiektowego Podstawy grafiki obiektowej Zagadnienia Grafika proceduralna grafika obiektowa Grafika WPF dualizm XAML C# Właściwości obiektów graficznych edycja właściwości

Bardziej szczegółowo

Tworzenie logo. Omówione zagadnienia

Tworzenie logo. Omówione zagadnienia Tworzenie logo Witamy w programie CorelDRAW, wszechstronnym programie do tworzenia rysunków wektorowych i projektów graficznych przeznaczonym dla profesjonalnych grafików. W niniejszym samouczku przedstawiono

Bardziej szczegółowo

Wstęp Pierwsze kroki Pierwszy rysunek Podstawowe obiekty Współrzędne punktów Oglądanie rysunku...

Wstęp Pierwsze kroki Pierwszy rysunek Podstawowe obiekty Współrzędne punktów Oglądanie rysunku... Wstęp... 5 Pierwsze kroki... 7 Pierwszy rysunek... 15 Podstawowe obiekty... 23 Współrzędne punktów... 49 Oglądanie rysunku... 69 Punkty charakterystyczne... 83 System pomocy... 95 Modyfikacje obiektów...

Bardziej szczegółowo

Druga aplikacja Prymitywy, alpha blending, obracanie bitmap oraz mały zestaw przydatnych funkcji wyświetlających własnej roboty.

Druga aplikacja Prymitywy, alpha blending, obracanie bitmap oraz mały zestaw przydatnych funkcji wyświetlających własnej roboty. Przyszedł czas na rysowanie własnych figur, czyli prymitywy, obracanie bitmap, oraz alpha blending-czyli półprzezroczystość. Będę opisywał tylko rzeczy nowe-nie ma potrzeby abym się powtarzał. Zaczynajmny

Bardziej szczegółowo

Przed rozpoczęciem pracy otwórz nowy plik (Ctrl +N) wykorzystując szablon acadiso.dwt

Przed rozpoczęciem pracy otwórz nowy plik (Ctrl +N) wykorzystując szablon acadiso.dwt Przed rozpoczęciem pracy otwórz nowy plik (Ctrl +N) wykorzystując szablon acadiso.dwt Zadanie: Utwórz szablon rysunkowy składający się z: - warstw - tabelki rysunkowej w postaci bloku (według wzoru poniżej)

Bardziej szczegółowo

Rysowanie prostych obiektów graficznych przy użyciu biblioteki AWT (Abstract Window Toolkit)

Rysowanie prostych obiektów graficznych przy użyciu biblioteki AWT (Abstract Window Toolkit) Rysowanie prostych obiektów graficznych przy użyciu biblioteki AWT (Abstract Window Toolkit) Biblioteka Abstrakcyjnych Narzędzi Okienkowych AWT (Abstract Window Toolkit) jako historycznie pierwsza w JDK

Bardziej szczegółowo

Podstawy Swing. Tomasz Borzyszkowski

Podstawy Swing. Tomasz Borzyszkowski Podstawy Swing Tomasz Borzyszkowski Wprowadzenie Już Java 1.0 zawierała bibliotekę AWT (Abstract Window Toolkit) służącą do oprogramowania GUI. Kolejne wersje Java również wspierały to rozwiązanie. Swing

Bardziej szczegółowo

Zajęcia nr 15 JavaScript wprowadzenie do JavaScript

Zajęcia nr 15 JavaScript wprowadzenie do JavaScript Zajęcia nr 15 JavaScript wprowadzenie do JavaScript Prowadzący: Andrzej Gąsienica-Samek, strona kółka www.atinea.pl/kolko Wprowadzenie do jsfiddle.net Uruchom Chrome i wejdź na stronę http://jsfiddle.net.

Bardziej szczegółowo

Multimedia i interfejsy. Ćwiczenie 5 HTML5

Multimedia i interfejsy. Ćwiczenie 5 HTML5 Multimedia i interfejsy Ćwiczenie 5 HTML5 Celem ćwiczenia jest poznanie nowych elementów wprowadzonych w HTML 5, do których należą m.in. video oraz canvas. Poniższy opis przedstawia sposób użycia tych

Bardziej szczegółowo

Tworzenie prostych obrazów wektorowych w programie CorelDRAW 12

Tworzenie prostych obrazów wektorowych w programie CorelDRAW 12 Tworzenie prostych obrazów wektorowych w programie CorelDRAW 12 Pakiet CorelDRAW Graphics Suite to zestaw aplikacji do tworzenia i edytowania grafiki wektorowej i rastrowej. Dwa najważniejsze składniki

Bardziej szczegółowo

W niniejszym samouczku przedstawiono sposób tworzenia logo dla wymyślonej kawiarni. Tak będzie wyglądać ostateczny efekt pracy:

W niniejszym samouczku przedstawiono sposób tworzenia logo dla wymyślonej kawiarni. Tak będzie wyglądać ostateczny efekt pracy: Tworzenie logo Witamy w programie CorelDRAW, wszechstronnym programie do tworzenia rysunków wektorowych i projektów graficznych przeznaczonym dla profesjonalnych grafików. W niniejszym samouczku przedstawiono

Bardziej szczegółowo

Narzędzia programu Paint

Narzędzia programu Paint Okno programu Paint Narzędzia programu Paint Na karcie Start znajduje się przybornik z narzędziami. Narzędzia te są bardzo przydatne w pracy z programem. Można nimi rysować i malować, kolorować i pisać,

Bardziej szczegółowo

GIMP Grafika rastrowa (Ćwiczenia cz. 2)

GIMP Grafika rastrowa (Ćwiczenia cz. 2) Zjazd 1 GIMP Grafika rastrowa (Ćwiczenia cz. 2) Zaznaczenia Aby zacząć profesjonalnie rysować w programie GIMP należy opanować tematykę zaznaczeń. Zaznaczenia (inaczej maski) służą do zaznaczania obszarów

Bardziej szczegółowo

8. Dynamiczne generowanie grafiki, cz. 2

8. Dynamiczne generowanie grafiki, cz. 2 8. Dynamiczne generowanie grafiki, cz. 2 8.1. Generowanie tekstu Chociaż tekst można umieścić na grafice korzystając z HTML (używając grafiki jako tła obiektu), często wygodniej jest umieścić tekst bezpośrednio

Bardziej szczegółowo

Główne elementy zestawu komputerowego

Główne elementy zestawu komputerowego Główne elementy zestawu komputerowego Monitor umożliwia oglądanie efektów pracy w programach komputerowych Mysz komputerowa umożliwia wykonywanie różnych operacji w programach komputerowych Klawiatura

Bardziej szczegółowo

TWORZENIE OBIEKTÓW GRAFICZNYCH

TWORZENIE OBIEKTÓW GRAFICZNYCH R O Z D Z I A Ł 2 TWORZENIE OBIEKTÓW GRAFICZNYCH Rozdział ten poświęcony będzie dokładnemu wyjaśnieniu, w jaki sposób działają polecenia służące do rysowania różnych obiektów oraz jak z nich korzystać.

Bardziej szczegółowo

Animacje z zastosowaniem suwaka i przycisku

Animacje z zastosowaniem suwaka i przycisku Animacje z zastosowaniem suwaka i przycisku Animacja Pole równoległoboku Naukę tworzenia animacji uruchamianych na przycisk zaczynamy od przygotowania stosunkowo prostej animacji, za pomocą, której można

Bardziej szczegółowo

Ćwiczenie 4: Edycja obiektów

Ćwiczenie 4: Edycja obiektów Ćwiczenie 4: Edycja obiektów Aplikacja ArcMap nadaje się do edycji danych równie dobrze jak do opracowywania map. W tym ćwiczeniu rozbudujesz drogę prowadzacą do lotniska łącząc jej przedłużenie z istniejącymi

Bardziej szczegółowo

CorelDraw - Edytor grafiki wektorowej

CorelDraw - Edytor grafiki wektorowej CorelDraw - Edytor grafiki wektorowej Rodzaje grafik Obecnie możemy spotkać się z dwoma rodzajami grafik: grafiką rastrową (zwaną również grafiką bitmapową) oraz grafiką wektorową. W grafice rastrowej

Bardziej szczegółowo

1 Wstęp teoretyczny. Temat: Obcinanie odcinków do prostokąta. Grafika komputerowa 2D. Instrukcja laboratoryjna Prostokąt obcinający

1 Wstęp teoretyczny. Temat: Obcinanie odcinków do prostokąta. Grafika komputerowa 2D. Instrukcja laboratoryjna Prostokąt obcinający Instrukcja laboratoryjna 3 Grafika komputerowa 2D Temat: Obcinanie odcinków do prostokąta Przygotował: dr inż. Grzegorz Łukawski, mgr inż. Maciej Lasota, mgr inż. Tomasz Michno 1 Wstęp teoretyczny 1.1

Bardziej szczegółowo

Następnie zdefiniujemy utworzony szkic jako blok, wybieramy zatem jak poniżej

Następnie zdefiniujemy utworzony szkic jako blok, wybieramy zatem jak poniżej Zadanie 1 Wykorzystanie opcji Blok, Podziel oraz Zmierz Funkcja Blok umożliwia zdefiniowanie dowolnego złożonego elementu rysunkowego jako nowy blok a następnie wykorzystanie go wielokrotnie w tworzonym

Bardziej szczegółowo

Edytor tekstu OpenOffice Writer Podstawy

Edytor tekstu OpenOffice Writer Podstawy Edytor tekstu OpenOffice Writer Podstawy Cz. 3. Rysunki w dokumencie Obiekt Fontwork Jeżeli chcemy zamieścić w naszym dokumencie jakiś efektowny napis, na przykład tytuł czy hasło promocyjne, możemy w

Bardziej szczegółowo

Przewodnik po obszarze roboczym

Przewodnik po obszarze roboczym Przewodnik po obszarze roboczym Witamy w programie CorelDRAW, wszechstronnym programie do tworzenia rysunków wektorowych i projektów graficznych przeznaczonym dla profesjonalnych grafików. Projekty tworzone

Bardziej szczegółowo

Podstawy Informatyki Wykład V

Podstawy Informatyki Wykład V Nie wytaczaj armaty by zabić komara Podstawy Informatyki Wykład V Grafika rastrowa Paint Copyright by Arkadiusz Rzucidło 1 Wprowadzenie - grafika rastrowa Grafika komputerowa tworzenie i przetwarzanie

Bardziej szczegółowo

FIGURY I BRYŁY JEDNOSTKI MIARY KĄTY POLE I OBWÓD OBJĘTOŚĆ I POWIERZCHNIA TRÓJKĄT PROSTOKĄTNY

FIGURY I BRYŁY JEDNOSTKI MIARY KĄTY POLE I OBWÓD OBJĘTOŚĆ I POWIERZCHNIA TRÓJKĄT PROSTOKĄTNY Wstęp Ten multimedialny program edukacyjny zawiera przykłady i zadania pozwalające na samodzielne ćwiczenie i sprawdzenie wiadomości w zakresie figur i brył geometrycznych dla klas 5-6 szkoły podstawowej

Bardziej szczegółowo

CorelDraw - obiekty tekstowe

CorelDraw - obiekty tekstowe CorelDraw - obiekty tekstowe Narzędzie Tekst wybieramy klawiszem F8 lub klikając w przyborniku na ikonie -. Kursor myszki zmieni swój kształt na - trybach:. Przy pomocy narzędzia Tekst możemy umieszczać

Bardziej szczegółowo

Rysowanie Części 2D. Lekcja Druga. Podczas tej lekcji przyjrzymy się jak wykonać poniższy rysunek przy pomocy programu BobCAD-CAM.

Rysowanie Części 2D. Lekcja Druga. Podczas tej lekcji przyjrzymy się jak wykonać poniższy rysunek przy pomocy programu BobCAD-CAM. Rysowanie Części 2D Lekcja Druga Podczas tej lekcji przyjrzymy się jak wykonać poniższy rysunek przy pomocy programu BobCAD-CAM. Musimy zdecydować najpierw jak rozpoczniemy rysowanie projektu. Rysunek

Bardziej szczegółowo

Szybkie tworzenie grafiki w GcIde

Szybkie tworzenie grafiki w GcIde Szybkie tworzenie grafiki w GcIde Opracował: Ryszard Olchawa Poniższy opis dotyczy aplikacji okienkowej w systemie Windows lub Linux bazującej na obiektowej bibliotece rofrm stworzonej w środowisku GcIde.

Bardziej szczegółowo

Ćwiczenie 2 Warstwy i kształty podstawowe

Ćwiczenie 2 Warstwy i kształty podstawowe Ćwiczenie 2 Warstwy i kształty podstawowe Poznamy podstawy pracy z nowym obrazkiem w Adobe Photoshop: - zapisywanie własnego ustawienia nowo tworzonego pliku - wybór kolorów, tworzenie własnych próbek

Bardziej szczegółowo

Pascal - grafika. Uruchomienie trybu graficznego. Moduł graph. Domyślny tryb graficzny

Pascal - grafika. Uruchomienie trybu graficznego. Moduł graph. Domyślny tryb graficzny Moduł graph Pascal - grafika Pascal zawiera standardowy moduł do tworzenia obiektów graficznych linii, punktów, figur geometrycznych itp. Chcąc go użyć należy w programie (w nagłówku) wstawić deklarację:

Bardziej szczegółowo

PWSG Ćwiczenia 12. Wszystkie ukończone zadania należy wysłać na adres: lub

PWSG Ćwiczenia 12. Wszystkie ukończone zadania należy wysłać na adres: lub PWSG Ćwiczenia 12 Wszystkie ukończone zadania należy wysłać na adres: sara.m.jurczyk@gmail.com lub sarajurczyk@kul.lublin.pl Zadanie 1: Różnica między zwykłymi polami/metodami, a polami/metodami static

Bardziej szczegółowo

4.6 OpenOffice Draw tworzenie ilustracji

4.6 OpenOffice Draw tworzenie ilustracji 4-82 4.6 OpenOffice Draw tworzenie ilustracji 4.6.1 Podstawowe informacje o grafice komputerowej Istnieją dwa rodzaje grafiki komputerowej: mapy bitowe (grafika rastrowa), grafiki wektorowe. Mapy bitowe

Bardziej szczegółowo

Obsługa grafiki w Delphi, rysowanie na płótnie, obsługa myszki, zapisywanie obrazków do plików, bitmapy pozaekranowe.

Obsługa grafiki w Delphi, rysowanie na płótnie, obsługa myszki, zapisywanie obrazków do plików, bitmapy pozaekranowe. Programowanie Wizualno-Obiektowe (studia zaoczne - inżynieria komputerowa) Zajęcia z Delphi 5, program 1 Temat: Zadanie: Obsługa grafiki w Delphi, rysowanie na płótnie, obsługa myszki, zapisywanie obrazków

Bardziej szczegółowo

O czym należy pamiętać?

O czym należy pamiętać? O czym należy pamiętać? Podczas pracy na płaszczyźnie możliwe jest wprowadzanie współrzędnych punktów w następujących układach: - układ współrzędnych kartezjańskich: x, y służy do rysowania odcinków o

Bardziej szczegółowo

INSTRUKCJA DO ĆWICZENIA 5

INSTRUKCJA DO ĆWICZENIA 5 INSTRUKCJA DO ĆWICZENIA 5 Kontynuacja tworzenia aplikacje umożliwiających oszacowanie szukanych wartości przez symulację doświadczenia losowego, z ilustracją graficzną jego wyników. Zadanie wykonamy dla

Bardziej szczegółowo

W tej instrukcji zostanie opisany sposób w jaki tworzy się, edytuje i usuwa obiekty na mapie. Następnie wybierz Rysuj

W tej instrukcji zostanie opisany sposób w jaki tworzy się, edytuje i usuwa obiekty na mapie. Następnie wybierz Rysuj Rysowanie, edycja, usuwanie Ogólnie W tej instrukcji zostanie opisany sposób w jaki tworzy się, edytuje i usuwa obiekty na mapie. Rysowanie punktu Obiekt na mapie składa się z punktów. Punkt również może

Bardziej szczegółowo

Wymagania edukacyjne - Informatyka w klasie I

Wymagania edukacyjne - Informatyka w klasie I Wymagania edukacyjne - Informatyka w klasie I Poziom niski wyrażony cyfrą 2 wymagania konieczne. Uczeń ma duże trudności ze spełnieniem wymagań, potrzebuje częstej pomocy nauczyciela. Poziom dostateczny

Bardziej szczegółowo

Techniki wstawiania tabel

Techniki wstawiania tabel Tabele w Wordzie Tabela w Wordzie to uporządkowany układ komórek w postaci wierszy i kolumn, w które może być wpisywany tekst lub grafika. Każda komórka może być formatowana oddzielnie. Możemy wyrównywać

Bardziej szczegółowo

Simba 3D LOGO. Cele zajęć: - Poznanie zasad i sposobów tworzenia procedur z parametrami. - Poznanie zasad wywoływania procedur z parametrami.

Simba 3D LOGO. Cele zajęć: - Poznanie zasad i sposobów tworzenia procedur z parametrami. - Poznanie zasad wywoływania procedur z parametrami. Simba 3D LOGO Scenariusz lekcji Dokument zawiera cykl proponowanych scenariuszy lekcji z wykorzystaniem programu dydaktycznego Simba 3D LOGO. Program ten oparty jest na edukacyjnym języku programowania

Bardziej szczegółowo

Ćwiczenie pochodzi ze strony

Ćwiczenie pochodzi ze strony Ćwiczenie pochodzi ze strony http://corel.durscy.pl/ Celem ćwiczenia jest poznanie właściwości obiektu Elipsa oraz możliwości tworzenia za pomocą niego rysunków. Dodatkowo, w zadaniu tym, ćwiczone są umiejętności

Bardziej szczegółowo

WSTĘP; NARZĘDZIA DO RYSOWANIA

WSTĘP; NARZĘDZIA DO RYSOWANIA 1 z 5 Link do instalacji Gimpa Gimp WSTĘP; NARZĘDZIA DO RYSOWANIA Menu w Gimpie znajduje się w oknie głównym Gimpa i w oknie obrazu. Dostępne jest również po kliknięciu prawym klawiszem myszy na obraz.

Bardziej szczegółowo

INSTYTUT INFORMATYKI STOSOWANEJ MODELOWANIE CZĘŚCI Z WYKORZYSTANIEM PROGRAMU SOLID EDGE

INSTYTUT INFORMATYKI STOSOWANEJ MODELOWANIE CZĘŚCI Z WYKORZYSTANIEM PROGRAMU SOLID EDGE INSTYTUT INFORMATYKI STOSOWANEJ MODELOWANIE CZĘŚCI Z WYKORZYSTANIEM PROGRAMU SOLID EDGE Łódź 2012 1 Program Solid Edge ST (Synchronous Technology) umożliwia projektowanie urządzeń technicznych w środowisku

Bardziej szczegółowo

Uwaga! CorelDRAW ćwiczenia kl. III Strona 1 z 6

Uwaga! CorelDRAW ćwiczenia kl. III Strona 1 z 6 Uwaga! Korzystaj z POMOCY programu CorelDRAW!!! Wpisz słowo kluczowe, które szukasz w odpowiednie miejsce (Zakładka POMOC- Tematy pomocy Indeks) Po wykonaniu każdego rysunku zgrupuj jego elementy (zaznacz

Bardziej szczegółowo

Górnicki Mateusz 17681

Górnicki Mateusz 17681 Streszczenie referatu pt.: Obróbka i montaż wideo w programie Sony Vegas -ścieżki audio/wideo, przejścia, filtry, rendering i inne Tytuł streszczenia: Maskowanie i animacja w programie Sony Vegas Pro Data

Bardziej szczegółowo

DARMOWA PRZEGLĄDARKA MODELI IFC

DARMOWA PRZEGLĄDARKA MODELI IFC www.bimvision.eu DARMOWA PRZEGLĄDARKA MODELI IFC BIM VISION. OPIS FUNKCJONALNOŚCI PROGRAMU. CZĘŚĆ I. Spis treści OKNO GŁÓWNE... 1 NAWIGACJA W PROGRAMIE... 3 EKRAN DOTYKOWY... 5 MENU... 6 ZAKŁADKA WIDOK....

Bardziej szczegółowo

PyX jest pakietem Pythona do grafiki wektorowej. Pozawala zatem tworzyd pliki EPS oraz PDF.

PyX jest pakietem Pythona do grafiki wektorowej. Pozawala zatem tworzyd pliki EPS oraz PDF. PyX jest pakietem Pythona do grafiki wektorowej. Pozawala zatem tworzyd pliki EPS oraz PDF. Aby go zainstalowad należy rozpakowad pakiet o nazwie PyX-0.10 do odpowiedniego katalogu. Będzie on dostępny

Bardziej szczegółowo

Bryła obrotowa, szyk kołowy, szyk liniowy

Bryła obrotowa, szyk kołowy, szyk liniowy Bryła obrotowa, szyk kołowy, szyk liniowy Zagadnienia. Tworzenie bryły obrotowej (dodawanie i odejmowanie bryły). Tworzenie rowków obwodowych. Tworzenie otworów powielonych za pomocą szyku kołowego. Wykorzystanie

Bardziej szczegółowo

Kod źródłowy programu: program Grafika1; uses crt, graph; (1) var sterownik, tryb:smallint; (2)

Kod źródłowy programu: program Grafika1; uses crt, graph; (1) var sterownik, tryb:smallint; (2) Grafika w Pascalu. Do tej pory, tworząc programy w Pascalu, wykorzystywaliśmy jedynie tryb tekstowy. Jednak Pascal, tak jak i inne języki programowania, umoŝliwia korzystanie równieŝ z trybu graficznego.

Bardziej szczegółowo

Użycie przestrzeni papieru i odnośników - ćwiczenie

Użycie przestrzeni papieru i odnośników - ćwiczenie Użycie przestrzeni papieru i odnośników - ćwiczenie Informacje ogólne Korzystanie z ćwiczeń Podczas rysowania w AutoCADzie, praca ta zwykle odbywa się w przestrzeni modelu. Przed wydrukowaniem rysunku,

Bardziej szczegółowo

Program graficzny MS Paint.

Program graficzny MS Paint. Program graficzny MS Paint. Program graficzny MS Paint (w starszych wersjach Windows Paintbrush lub mspaint) aplikacja firmy Microsoft w systemach Windows służąca do obróbki grafiki. SP 8 Lubin Zdjęcie:

Bardziej szczegółowo

Tematy lekcji informatyki klasa 4a styczeń 2013

Tematy lekcji informatyki klasa 4a styczeń 2013 Tematy lekcji informatyki klasa 4a styczeń 2013 temat 7. z podręcznika (str. 70-72); sztuczki 4. i 5. (str. 78); Narysuj ikony narzędzi do zaznaczania i opisz je. 19 Zaznaczamy fragment rysunku i przenosimy

Bardziej szczegółowo

Rys.1. Technika zestawiania części za pomocą polecenia WSTAWIAJĄCE (insert)

Rys.1. Technika zestawiania części za pomocą polecenia WSTAWIAJĄCE (insert) Procesy i techniki produkcyjne Wydział Mechaniczny Ćwiczenie 3 (2) CAD/CAM Zasady budowy bibliotek parametrycznych Cel ćwiczenia: Celem tego zestawu ćwiczeń 3.1, 3.2 jest opanowanie techniki budowy i wykorzystania

Bardziej szczegółowo