Створюйте власний платформо-незалежний код для побудови X-Y графіків, стовпчикові діаграм і т.п.
Створення схем і графіків на платформі Java ™ завжди викликало інтерес розробників. Традиційно Java-розробники використовували для цього java.awt.Graphics або Java 2D API. Деякі, можливо, застосовують готові інструменти з відкритими початковими кодами, наприклад, JSci. Однак багато хто з доступних варіантів обмежують вас вибором AWT або Swing. Щоб мінімізувати залежність від інструментальних засобів сторонніх постачальників або спростити малювання графіків, розгляньте можливість застосування Draw2D і напишіть свій власний код для креслення.
Indiver Dwivedi. Cтарший інженер-програміст, IBM India
У двох словах про Draw2D
DrawD є спрощену систему графічних елементів, розміщених на SWT Composite. Примірник Draw2D складається з SWT Composite, спрощеної системи і графічних примітивів. Графічні примітиви є будівельними блоками Draw2D. Повна інформація по Draw2D API знаходиться в системі допомоги Eclipse в "Посібнику розробника Draw2D". Оскільки в цій статті ми не збираємося навчати Draw2D, досить того, щоб ви розуміли, що Draw2D API допомагає малювати образи на SWT Canvas. Ви можете використовувати стандартні графічні примітиви, наприклад, Ellipse, Polyline, RectangleFigure і Triangle безпосередньо; ви можете розширити їх, створюючи свої власні графічні примітиви. Крім того, деякі примітиви-контейнери, наприклад Panel, можуть виступати в якості загального контейнера для всіх дочірніх елементів.
DrawD має два важливих пакета: org.eclipse.draw2d.geometry і org.eclipse.draw2d.graph, які я використовую в даній статті. Пакет org.eclipse.draw2d.geometry містить такі корисні класи як, наприклад, Rectangle. Point і PointList. які не потребують пояснень. Розробники, можливо, не працювали щільно з іншим пакетом org.eclipse.draw2d.graph. Цей пакет надає деякі важливі класи, такі як DirectedGraph. Node. Edge. NodeList і EdgeList. що допомагають при створенні графіків.
У цій статті, я поясню, як використовувати Draw2D для написання коду, який допомагає вам візуалізувати ваші дані в графічному вигляді. Я почну з опису техніки масштабування значень даних, що належать одному діапазону (наприклад від 0 до 2048) в їх еквівалентні значення, що належать іншій діапазону (наприклад, від 0 до 100). Потім я проілюструю, як намалювати графік X-Y з будь-якої кількості послідовностей, кожна з яких містить набір елементів даних. Після вивчення загальних понять ви легко зможете намалювати інші типи графіків, наприклад, секторні та стовпчикові діаграми.
Процес малювання графіка крок за кроком
Крок 1: Що ви хочете намалювати?
Очевидно, ви хочете вивести у вигляді графіка дані з якого-небудь джерела даних. Тобто, нам необхідні дані для візуалізації в графічному форматі. Для стислості, замість читання даних з XML-файла або якого-небудь іншого джерела даних, я згенерував дані в простій функції з назвою dataGenerator. яка використовує цикл for (;;) і повертає згенеровані значення у вигляді списку масивів.
Лістинг 1. Генерування даних
Крок 2: Техніка масштабування - генерування координат X і Y з наявних даних
нові поняття
FigureCanvas в Draw2D є розширенням SWT Canvas. FigureCanvas може містити графічні примітиви Draw2D.
Panel є контейнером графічних примітивів загального призначення в Draw2D, який може містити дочірні примітиви. Ви можете додати безліч примітивів на одну Panel і потім додати цей один елемент Panel в FigureCanvas.
DirectedGraph є 2-D-графіком, що має кінцеве число елементів Node. Кожен Node розташований на деякій Point. суміжні Node пов'язані (або з'єднані) один з одним за допомогою Edges.
Коли ви бажаєте намалювати графік на 2-D площині, ви повинні знайти координати X і Y кожної точки. Магія малювання графіків полягає в здатності масштабувати наявні дані з одного діапазону в інший, тобто, для набору значень, наприклад, ви повинні точно вирішити, які точки (в координатах X і Y) на 2-D площині представляють значення 10, 20 і 30 .
Завжди малюйте в фіксованому масштабі. Іншими словами, ви можете намалювати будь-яку кількість точок в обмеженій області. Оскільки область фіксована, ви можете завжди знайти інтервал (довжину) по осі X і інтервал (висоту) по осі Y. Знання інтервалів по осі X і Y - це тільки одна частина рівняння. Інша частина - визначити спектр значень даних і визначення координат кожного значення, знайшовши еквівалентну йому значення в новому діапазоні.
Обчислення координат X і Y
X-координати. X-координата - це відстань до точки по горизонталі від початку координат. Відстані по горизонталі для всіх точок в наборі обчислюються простим підрахунком кількості елементів і діленням довжини осі X на n сегментів, де n дорівнює кількості елементів в даному наборі. В результаті цього поділу ми отримуємо довжину кожного сегмента. Перша точка в наборі розташовується на відстані, рівному довжині сегмента. Кожна наступна точка розташовується на відстані довжини сегмента плюс відстань від початку координат до попередньої точки.
Наприклад, для набору ви відразу бачите, що потрібно намалювати чотири точки, оскільки набір містить чотири значення. Таким чином, вісь X повинна бути поділена на чотири однакових сегмента з довжиною length = span / 4. Таким чином, якщо довжина осі X дорівнює 800, довжина сегмента буде дорівнює 800/4, або 200. X-координата першого елемента (10) буде дорівнює 200, другого елемента (20) - 400 і т.д.
Лістинг 2. Обчислення X-координат
Y-координати. Y-координата - це відстань до точки від початку координат по вертикалі. Обчислення Y-координат полягає в масштабуванні значень з одного діапазону в інший. Наприклад, для набору значень, видно, що діапазон даних дорівнює від 0 до 40, а новий діапазон - це довжина (висота) осі Y. Припустивши, що висота осі Y дорівнює 400, еквівалентна висота першого елемента (10) буде дорівнює 100, другого елемента (20) - 200 і т.д.
Ви зможете краще зрозуміти процес масштабування значень з одного діапазону в інший на наступному прикладі. Припустимо, що один діапазон значень дорівнює 0-2048, і ви хочете плавно змінювати масштаб будь-яке значення з цього діапазону (наприклад, 1024) в інший діапазон, рівний 0-100. Ви відразу знайдете результат - еквівалентне значення дорівнює 50. В масштабування беруть участь наступні три рядки арифметичних дій:
Крок 3: Де ви хочете малювати?
Вам необхідна якась область для малювання. Створіть ваш власний вид (view), розширивши Eclipse ViewPart і використовуючи SWT Composite. В якості альтернативи ви можете використовувати SWT-оболонку, що викликається з функції m ain ().
При розширенні Eclipse ViewPart ви повинні реалізувати принаймні дві функції: createPartControl (Composite parent) і setFocus (). Функція createPartControl (Composite parent) викликається автоматично, коли ваш вигляд повинен бути намальований на екрані. Вам цікавий тільки отриманий таким чином SWT Composite. Передайте його в клас, який ви написали для креслення графіків.
Лістинг 3. Використання Eclipse ViewPart для малювання
Крок 4: Який тип графіка вам потрібен?
Якщо ви маєте дані і область для малювання, ви повинні вирішити, який тип візуалізації вам потрібен. У даній статті я продемонструю, як написати код для створення лінійного графіка. Якщо ви зрозумієте техніку, що лежить в основі малювання лінійного графіка, то зможете намалювати і інші типи графіків, наприклад секторні і стовпчикові діаграми. Для вивчення способу побудови лінійного графіка подивіться клас DirectedGraphXYPlotter. який я написав для цієї статті. (Див. Файл \ src \ GraFix \ Plotters \ DirectedGraphXYPlotter.java в вихідному коді, прикріпленому до цієї статті).
Крок 5: Створення вашого власного будівника X-Y-графіків
Будівник X-Y-графіків повинен вміти малювати будь-яке число ліній на 2-D площині. Кожна лінія повинна графічно показувати позицію кожної точки своєї послідовності з посиланням на вісь X і на вісь Y. Кожна точка повинна бути з'єднана з наступною точкою послідовності лінією. Ви можете створити такий будівник, використовуючи графічні примітиви Draw2D, що представляють точку і лінію. Наприклад, для представлення точки я створив примітив Dot, розширивши примітив Ellipse, і використовував PolylineConnection для подання сполучних ліній.
Клас DirectedGraphXYPlotter має тільки дві функції зі специфікатором доступу public: setData (ArrayList seriesData) і plot (). Функція setData (ArrayList seriesData) приймає дані (див. Крок 1), які ви хочете візуалізувати графічно, а функція plot () починає малювати графік.
Після виклику функції plot () необхідно послідовно виконати наступні дії:
- Візьміть SWT Composite і покладіть на нього FigureCanvas. Потім на канву покладіть примітив-контейнер загального призначення, наприклад Panel.
- Порахуйте кількість послідовностей для виведення графіка і заповніть необхідну кількість NodeLists і EdgeLists для створення DirectedGraphs.
- Намалюйте осі X і Y на примітиві Panel. (Див. Файли XRulerBar.java і YRulerBar.java в каталозі \ src \ GraFix \ Figure прикріпленого до статті вихідного коду).
- Створіть стільки DirectedGraphs. скільки є послідовностей для малювання.
- Намалюйте точки і сполучні лінії на примітиві Panel, отримуючи дані графіка з DirectedGraphs. створеного на кроці d.
- Нарешті, встановіть вміст канви, надавши примітив Panel, який містить всі точки і сполучні лінії, підготовлені вами.
У коді, наведеному нижче:
- Рядки 6-11 відповідає кроку a.
- Рядок 14, функція populateNodesAndEdges (). відповідає кроку b.
- Рядок 16, функція drawAxis (), відповідає кроку c.
- Рядки 17, 18 і 19 відповідають крокам d і e.
- Рядок 20 відповідає кроку f.
Лістинг 4. Функція plot ()
Дві важливі внутрішні функції, що викликаються в plot (). populateNodesAndEdges () і drawDotsAndConnections () допомагають в малюванні точок. Перед тим як ви дізнаєтеся, що точно роблять ці функції, розглянемо DirectedGraph.
Що таке DirectedGraph? Для малювання графіка в Draw2D ви спочатку повинні створити графік віртуально, щоб визначити виведені точки і лінії. Після створення цього графіка ви можете використовувати його в подальшому для фактичного початку малювання примітивів на канві. Ви можете візуалізувати DirectedGraph у вигляді 2-D-графіка, що має кінцеве число елементів Node; кожен елемент Node розташований в деякій точці (Point); суміжні елементи Nodes пов'язані (або з'єднані) один з одним елементами Edges.
Ви можете зрозуміти загадку створення DirectedGraph за допомогою наступних рядків коду. Перш за все, створіть список елементів Nodes і список елементів Edges. Потім створіть новий DirectedGraph і вкажіть в якості його членів (Nodes і Edges) щойно створені списки NodeList і EdgeList. Використовуйте GraphVisitor для звернення до цього DirectedGraph. Для спрощення роботи пакет org.eclipse.draw2d.internal.graph містить багато реалізацій GraphVisitor. які вже мають спеціалізовані алгоритми для звернення до графіку.
Отже, приклад коду для включення DirectedGraph може бути таким:
Лістинг 5. Приклад DirectedGraph
Тепер, коли ви знаєте, що DirectedGraph містить список елементів Nodes. в якому кожен Node може містити деякі дані і зберігати їх координати X і Y, і список елементів Edges. в якому кожен Edge знає про двох елементах Node на обох кінцях, ви можете використовувати цю інформацію для малювання графіків, застосовуючи наступну методику, що складається з двох частин:
Частина A - Заповнити Nodes і Edges шляхом:
- Створення NodeList. містить по одному Node на елемент набору. Наприклад, для набору потрібно створити чотири Node.
- Пошуку координат X і Y для кожного елемента і збереження їх в змінних екземпляра членах node.x і node.y.
- Створення EdgeList. що містить n-1 елементів Edges. де n - кількість елементів в наборі. Наприклад, для набору потрібно створити три Edges.
- Вказівки Node для лівого і правого краю кожного Edge і установки змінних екземпляра edge.start і edge.end відповідно.
Частина B - Намалювати графічні примітиви, представлені Nodes і Edges шляхом:
- Малювання примітиву Dot, представленого кожним Node.
- Малювання примітиву PolylineConnection, представленого кожним Edge.
- Прив'язування кожного примітиву PolylineConnection до примітивів Dot зліва і справа.
Тепер повернемося до роботи з внутрішніми функціями:
- Функція populateNodesAndEdges () реалізує частину A розглянутої методики, а drawDotsAndConnections () реалізує частину B.
- Функція populateNodesAndEdges () вважає кількість послідовностей, призначених для виведення на графік. Вона створює один NodeList і один EdgeList для кожної послідовності.
- Кожен NodeList містить список Nodes для конкретної послідовності. Кожен Node зберігає інформацію про координати X і Y. Функції getXCoordinates () і getYCoordinates () витягають значення координат X і Y відповідно. Ці функції також масштабують значення даних з одного діапазону в інший, використовуючи алгоритм, описаний на кроці 2.
- Кожен EdgeList містить список Edges для конкретної послідовності. Кожен Edge містить Node зліва і інший Node справа.
Лістинг 6. Функція populateNodesAndEdges ()
Після того, як функція populateNodesAndEdges () виконає свою роботу зі створення NodeLists і EdgeLists для всіх послідовностей, інша функція drawDotsAndConnections () починає малювати графічні примітиви Dot для кожного Node і примітиви PolylineConnection для кожного Edge.
Лістинг 7. Функції drawDotsAndConnections (), drawNode () і drawEdge ()
Результат малювання графіка
Якщо вам знадобитися представити дані в графічному вигляді, Draw2D є хорошим інструментом. Використання Draw2D для написання вашого власного Java-коду, що виконує малювання діаграм і графіків, може допомогти вам сконцентруватися на коді масштабування і малювання, залишаючи для Draw2D і SWT роботу по рендерингу і висновку на екран. Ви можете також контролювати зовнішній вигляд ваших графіків, використовуючи свій вибір графічні примітиви Draw2D. Draw2D спрощує основну роботу з малювання діаграм і графіків, а також мінімізує вашу залежність від інструментальних програм сторонніх постачальників.