Статьи

Створення графіків з використанням Draw2D і SWT на платформі Java

  1. У двох словах про Draw2D DrawD є спрощену систему графічних елементів, розміщених на SWT Composite....
  2. Лістинг 1. Генерування даних
  3. Крок 2: Техніка масштабування - генерування координат X і Y з наявних даних
  4. Лістинг 2. Обчислення X-координат
  5. Крок 3: Де ви хочете малювати?
  6. Лістинг 3. Використання Eclipse ViewPart для малювання
  7. Крок 4: Який тип графіка вам потрібен?
  8. Крок 5: Створення вашого власного будівника XY-графіків
  9. Лістинг 4. Функція plot ()
  10. Лістинг 5. Приклад DirectedGraph
  11. Лістинг 6. Функція populateNodesAndEdges ()
  12. Лістинг 7. Функції drawDotsAndConnections (), drawNode () і drawEdge ()
  13. Результат малювання графіка
  14. підсумки

У двох словах про 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). Потім я проілюструю, як намалювати графік XY з будь-якої кількості послідовностей, кожна з яких містить набір елементів даних. Після вивчення загальних понять ви легко зможете намалювати інші типи графіків, наприклад, секторні та стовпчикові діаграми.

На початок

Процес малювання графіка крок за кроком

Крок 1: Що ви хочете намалювати?

Очевидно, ви хочете вивести у вигляді графіка дані з якого-небудь джерела даних. Тобто, нам необхідні дані для візуалізації в графічному форматі. Для стислості, замість читання даних з XML-файла або якого-небудь іншого джерела даних, я згенерував дані в простій функції з назвою dataGenerator, яка використовує цикл for (;;) і повертає згенеровані значення у вигляді списку масивів.

Лістинг 1. Генерування даних

private ArrayList dataGenerator () {double series1 [] = new double [5]; for (int i = 0; i <series1.length; i ++) series1 [i] = (i * 10) + 10; // a linear series containing 10,20,30,40,50 double series2 [] = new double [9]; series2 [0] = 20; series2 [1] = 150; series2 [2] = 5; series2 [3] = 90; series2 [4] = 35; series2 [5] = 20; series2 [6] = 150; series2 [7] = 5; series2 [8] = 45; double series3 [] = new double [7]; for (int i = 0; i <series3.length; i ++) series3 [i] = (i * 20) + 15; seriesData.add (series1); seriesData.add (series2); seriesData.add (series3); returnseriesData; }

Крок 2: Техніка масштабування - генерування координат X і Y з наявних даних

Коли ви бажаєте намалювати графік на 2-D площині, ви повинні знайти координати X і Y кожної точки. Магія малювання графіків полягає в здатності масштабувати наявні дані з одного діапазону в інший, тобто, для набору значень, наприклад {10,20,30}, ви повинні точно вирішити, які точки (в координатах X і Y) на 2-D площині представляють значення 10, 20 і 30.

Завжди малюйте в фіксованому масштабі. Іншими словами, ви можете намалювати будь-яку кількість точок в обмеженій області. Оскільки область фіксована, ви можете завжди знайти інтервал (довжину) по осі X і інтервал (висоту) по осі Y. Знання інтервалів по осі X і Y - це тільки одна частина рівняння. Інша частина - визначити спектр значень даних і визначення координат кожного значення, знайшовши еквівалентну йому значення в новому діапазоні.

Обчислення координат X і Y

X-координати: X-координата - це відстань до точки по горизонталі від початку координат. Відстані по горизонталі для всіх точок в наборі обчислюються простим підрахунком кількості елементів і діленням довжини осі X на n сегментів, де n дорівнює кількості елементів в даному наборі. В результаті цього поділу ми отримуємо довжину кожного сегмента. Перша точка в наборі розташовується на відстані, рівному довжині сегмента. Кожна наступна точка розташовується на відстані довжини сегмента плюс відстань від початку координат до попередньої точки.

Наприклад, для набору {10,20,30,40} ви відразу бачите, що потрібно намалювати чотири точки, оскільки набір містить чотири значення. Таким чином, вісь X повинна бути поділена на чотири однакових сегмента з довжиною length = span / 4. Таким чином, якщо довжина осі X дорівнює 800, довжина сегмента буде дорівнює 800/4, або 200. X-координата першого елемента (10) буде дорівнює 200, другого елемента (20) - 400 і т.д.

Лістинг 2. Обчислення X-координат

private int [] getXCoordinates (ArrayList seriesData) {int xSpan = (int) GraFixConstants.xSpan; int longestSeries = Utilities.getLongestSeries (seriesData); int numSegments = ((double []) seriesData.get (longestSeries)). length; int sectionWidth = (int) xSpan / numSegments; // want to divide span of xAxis int xPositions [] = new int [numSegments]; // will contain X-coordinate of all dots. for (int i = 0; i <numSegments; i ++) {xPositions [i] = (i + 1) * sectionWidth; // dots spaced at distance of sectionWidth} return xPositions; }

Y-координати: Y-координата - це відстань до точки від початку координат по вертикалі. Обчислення Y-координат полягає в масштабуванні значень з одного діапазону в інший. Наприклад, для набору значень {10,20,30,40}, видно, що діапазон даних дорівнює від 0 до 40, а новий діапазон - це довжина (висота) осі Y. Припустивши, що висота осі Y дорівнює 400, еквівалентна висота першого елемента (10) буде дорівнює 100, другого елемента (20) - 200 і т.д.

Ви зможете краще зрозуміти процес масштабування значень з одного діапазону в інший на наступному прикладі. Припустимо, що один діапазон значень дорівнює 0-2048, і ви хочете плавно змінювати масштаб будь-яке значення з цього діапазону (наприклад, 1024) в інший діапазон, рівний 0-100. Ви відразу знайдете результат - еквівалентне значення дорівнює 50. В масштабування беруть участь наступні три рядки арифметичних дій:

рядок 1 ---> 2048 / одна тисяча двадцять чотири одно 2. рядок 2 ---> 100 - 0 равно100. рядок 3 ---> 100/2 дорівнює 50, що і є необхідним масштабованим значенням.

Крок 3: Де ви хочете малювати?

Вам необхідна якась область для малювання. Створіть ваш власний вид (view), розширивши Eclipse ViewPart і використовуючи SWT Composite. В якості альтернативи ви можете використовувати SWT-оболонку, що викликається з функції main ().

При розширенні Eclipse ViewPart ви повинні реалізувати принаймні дві функції: createPartControl (Composite parent) і setFocus (). Функція createPartControl (Composite parent) викликається автоматично, коли ваш вигляд повинен бути намальований на екрані. Вам цікавий тільки отриманий таким чином SWT Composite. Передайте його в клас, який ви написали для креслення графіків.

Лістинг 3. Використання Eclipse ViewPart для малювання

public class MainGraFixView extends ViewPart {public void createPartControl (Composite parent) {// створити або отримати дані в arraylist ArrayList seriesData = dataGenerator (); // створити екземпляр будівника графіків, і надати йому дані. DirectedGraphXYPlotter dgXYGraph = new DirectedGraphXYPlotter (parent); dgXYGraph.setData (seriesData); dgXYGraph.plot (); // запит на малювання} public void setFocus () {}}

Крок 4: Який тип графіка вам потрібен?

Якщо ви маєте дані і область для малювання, ви повинні вирішити, який тип візуалізації вам потрібен. У даній статті я продемонструю, як написати код для створення лінійного графіка. Якщо ви зрозумієте техніку, що лежить в основі малювання лінійного графіка, то зможете намалювати і інші типи графіків, наприклад секторні і стовпчикові діаграми. Для вивчення способу побудови лінійного графіка подивіться клас DirectedGraphXYPlotter, який я написав для цієї статті. (Див. Файл \ src \ GraFix \ Plotters \ DirectedGraphXYPlotter.java в вихідному коді, прикріпленому до цієї статті).

Крок 5: Створення вашого власного будівника XY-графіків

Будівник XY-графіків повинен вміти малювати будь-яке число ліній на 2-D площині. Кожна лінія повинна графічно показувати позицію кожної точки своєї послідовності з посиланням на вісь X і на вісь Y. Кожна точка повинна бути з'єднана з наступною точкою послідовності лінією. Ви можете створити такий будівник, використовуючи графічні примітиви Draw2D, що представляють точку і лінію. Наприклад, для представлення точки я створив примітив Dot, розширивши примітив Ellipse, і використовував PolylineConnection для подання сполучних ліній.

Клас DirectedGraphXYPlotter має тільки дві функції зі специфікатором доступу public: setData (ArrayList seriesData) і plot (). Функція setData (ArrayList seriesData) приймає дані (див. Крок 1), які ви хочете візуалізувати графічно, а функція plot () починає малювати графік.

Після виклику функції plot () необхідно послідовно виконати наступні дії:

  1. Візьміть SWT Composite і покладіть на нього FigureCanvas. Потім на канву покладіть примітив-контейнер загального призначення, наприклад Panel.
  2. Порахуйте кількість послідовностей для виведення графіка і заповніть необхідну кількість NodeLists і EdgeLists для створення DirectedGraphs.
  3. Намалюйте осі X і Y на примітиві Panel. (Див. Файли XRulerBar.java і YRulerBar.java в каталозі \ src \ GraFix \ Figure прикріпленого до статті вихідного коду).
  4. Створіть стільки DirectedGraphs, скільки є послідовностей для малювання.
  5. Намалюйте точки і сполучні лінії на примітиві Panel, отримуючи дані графіка з DirectedGraphs, створеного на кроці d.
  6. Нарешті, встановіть вміст канви, надавши примітив Panel, який містить всі точки і сполучні лінії, підготовлені вами.

У коді, наведеному нижче:

  • Рядки 6-11 відповідає кроку a.
  • Рядок 14, функція populateNodesAndEdges (), відповідає кроку b.
  • Рядок 16, функція drawAxis (), відповідає кроку c.
  • Рядки 17, 18 і 19 відповідають крокам d і e.
  • Рядок 20 відповідає кроку f.
Лістинг 4. Функція plot ()

1. public void plot () {2. // повернути управління, якщо ніде малювати, чи ні даних для малювання. 3. if (null == _ parent || null == _ seriesData) 4. return; 5. 6. Composite composite = new Composite (_parent, SWT.BORDER); 7. composite.setLayout (new FillLayout ()); 8. FigureCanvas canvas = new FigureCanvas (composite); 9. 10. Panel contents = new Panel (); // Panel - примітив-контейнер загального призначення 11. contents.setLayoutManager (new XYLayout ()); 12. initializeSpan (contents.getClientArea ()); 13. 14. populateNodesAndEdges (); 15. 16. drawAxis (contents); 17. for (int i = 0; i <_numSeries; i ++) {18. drawDotsAndConnections (contents, getDirectedGraph (i)); // намалювати точки і сполучні лінії 19.} 20. canvas.setContents (contents); 21.}

Дві важливі внутрішні функції, що викликаються в 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

// Це приклад, ви повинні додати реальні елементи Node (s) в цей список NodeList. NodeList nodes = new NodeList (); // створити список nodes. // Це приклад, ви повинні додати реальні елементи Edge (s) в цей список EdgeList. EdgeList edges = new EdgeList (); // створити список edges. DirectedGraph graph = new DirectedGraph (); graph.nodes = nodes; graph.edges = edges; new BreakCycles (). visit (graph); // запит в BreakCycles для звернення до графіку. // тепер наш "графік" готовий до використання.

Тепер, коли ви знаєте, що DirectedGraph містить список елементів Nodes, в якому кожен Node може містити деякі дані і зберігати їх координати X і Y, і список елементів Edges, в якому кожен Edge знає про двох елементах Node на обох кінцях, ви можете використовувати цю інформацію для малювання графіків, застосовуючи наступну методику, що складається з двох частин:

Частина A - Заповнити Nodes і Edges шляхом:

  • Створення NodeList, що містить по одному Node на елемент набору. Наприклад, для набору {10,20,30,40} потрібно створити чотири Node.
  • Пошуку координат X і Y для кожного елемента і збереження їх в змінних екземпляра членах node.x і node.y.
  • Створення EdgeList, що містить n-1 елементів Edges, де n - кількість елементів в наборі. Наприклад, для набору {10,20,30,40} потрібно створити три 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 ()

private void populateNodesAndEdges () {_seriesScaledValues ​​= new ArrayList (getScaledValues ​​(_seriesData)); _nodeLists = new ArrayList (); _edgeLists = new ArrayList (); for (int i = 0; i <_numSeries; i ++) {_nodeLists.add (new NodeList ()); // один NodeList на послідовність. _edgeLists.add (new EdgeList ()); // один EdgeList на послідовність. } // заповнити всі NodeLists елементами Nodes. for (int i = 0; i <_numSeries; i ++) {// для кожної послідовності double data [] = (double []) _ seriesData.get (i); // отримати послідовність int xCoOrds [] = getXCoordinates (_seriesData); int yCoOrds [] = getYCoordinates (i, data); // кожен NodeList буде містити стільки Nodes, скільки є точок в послідовності for (int j = 0; j <data.length; j ++) {Double doubleValue = new Double (data [j]); Node node = new Node (doubleValue); node.x = xCoOrds [j]; node.y = yCoOrds [j]; ((NodeList) _nodeLists.get (i)). Add (node); }} // заповнити всі EdgeLists елементами Edges. for (int i = 0; i <_numSeries; i ++) {NodeList nodes = (NodeList) _nodeLists.get (i); for (int j = 0; j <nodes.size () - 1; j ++) {Node leftNode = nodes.getNode (j); Node rightNode = nodes.getNode (j + 1); Edge edge = new Edge (leftNode, rightNode); edge.start = new Point (leftNode.x, leftNode.y); edge.end = new Point (rightNode.x, rightNode.y); ((EdgeList) _edgeLists.get (i)). Add (edge); }} Int breakpoint = 0; }

Після того, як функція populateNodesAndEdges () виконає свою роботу зі створення NodeLists і EdgeLists для всіх послідовностей, інша функція drawDotsAndConnections () починає малювати графічні примітиви Dot для кожного Node і примітиви PolylineConnection для кожного Edge.

Лістинг 7. Функції drawDotsAndConnections (), drawNode () і drawEdge ()

private void drawDotsAndConnections (IFigure contents, DirectedGraph graph) {for (int i = 0; i <graph.nodes.size (); i ++) {Node node = graph.nodes.getNode (i); drawNode (contents, node); } For (int i = 0; i <graph.edges.size (); i ++) {Edge edge = graph.edges.getEdge (i); drawEdge (contents, edge); }} Private void drawNode (IFigure contents, Node node) {Dot dotFigure = new Dot (); node.data = dotFigure; int xPos = node.x; int yPos = node.y; contents.add (dotFigure); contents.setConstraint (dotFigure, new Rectangle (xPos, yPos, -1, -1)); } Private void drawEdge (IFigure contents, Edge edge) {PolylineConnection wireFigure = new PolylineConnection (); //edge.source є елементом Node зліва EllipseAnchor sourceAnchor = new EllipseAnchor ((Dot) edge.source.data); //edge.target є елементом Node справа EllipseAnchor targetAnchor = new EllipseAnchor ((Dot) edge.target.data); wireFigure.setSourceAnchor (sourceAnchor); wireFigure.setTargetAnchor (targetAnchor); contents.add (wireFigure); }

Результат малювання графіка

На початок

підсумки

Якщо вам знадобитися представити дані в графічному вигляді, Draw2D є хорошим інструментом. Використання Draw2D для написання вашого власного Java-коду, що виконує малювання діаграм і графіків, може допомогти вам сконцентруватися на коді масштабування і малювання, залишаючи для Draw2D і SWT роботу по рендерингу і висновку на екран. Ви можете також контролювати зовнішній вигляд ваших графіків, використовуючи свій вибір графічні примітиви Draw2D. Draw2D спрощує основну роботу з малювання діаграм і графіків, а також мінімізує вашу залежність від інструментальних програм сторонніх постачальників.

На початок

Завантаження

Крок 3: Де ви хочете малювати?
Що таке DirectedGraph?

Новости