Source Code Cross Referenced for XGraph.java in  » XML-UI » xui32 » com » xoetrope » swing » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » XML UI » xui32 » com.xoetrope.swing 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        package com.xoetrope.swing;
0002:
0003:        import java.awt.BasicStroke;
0004:        import java.awt.BorderLayout;
0005:        import java.awt.geom.Arc2D;
0006:        import java.util.Vector;
0007:
0008:        import java.awt.Color;
0009:        import java.awt.Dimension;
0010:        import java.awt.Font;
0011:        import java.awt.FontMetrics;
0012:        import java.awt.GradientPaint;
0013:        import java.awt.Graphics;
0014:        import java.awt.Graphics2D;
0015:        import java.awt.Image;
0016:        import java.awt.Point;
0017:        import java.awt.RenderingHints;
0018:        import java.awt.Stroke;
0019:        import java.awt.event.ActionEvent;
0020:        import java.awt.event.ActionListener;
0021:        import java.awt.event.MouseEvent;
0022:        import java.awt.event.MouseMotionListener;
0023:        import java.awt.font.TextAttribute;
0024:        import java.awt.geom.AffineTransform;
0025:        import java.awt.geom.Area;
0026:        import java.awt.geom.Ellipse2D;
0027:        import java.awt.geom.GeneralPath;
0028:        import java.awt.geom.Rectangle2D;
0029:        import java.text.AttributedString;
0030:        import java.text.DecimalFormat;
0031:        import java.util.ArrayList;
0032:        import java.util.Enumeration;
0033:        import java.util.Hashtable;
0034:        import javax.swing.JComponent;
0035:        import javax.swing.JLayeredPane;
0036:        import javax.swing.Timer;
0037:        import net.xoetrope.xui.XAttributedComponent;
0038:        import net.xoetrope.xui.XModelHolder;
0039:        import net.xoetrope.xui.XProject;
0040:
0041:        import net.xoetrope.xui.data.XModel;
0042:        import net.xoetrope.xui.helper.XTranslator;
0043:        import net.xoetrope.xui.XProjectManager;
0044:
0045:        /**
0046:         * @todo render the entire chart (other than the background) as an image and
0047:         * then draw it as an alpha composite for smooter animation. For scaling the
0048:         * same couple be applicaed with a scale transformation, instead of redrawing
0049:         * the entire image each time.
0050:         */
0051:
0052:        /**
0053:         * An component that displays data as charts/graphs. The display can be Pie,
0054:         * Scatter, Line, X-Y or Bar charts
0055:         *
0056:         * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
0057:         * the GNU Public License (GPL), please see license.txt for more details. If
0058:         * you make commercial use of this software you must purchase a commercial
0059:         * license from Xoetrope.</p>
0060:         * <p> $Revision: 1.29 $</p>
0061:         */
0062:        public class XGraph extends JComponent implements  ActionListener,
0063:                XAttributedComponent, XModelHolder {
0064:            /**
0065:             * Bar chart - with one bar per value in each series
0066:             */
0067:            public final static int BARCHART = 0;
0068:
0069:            /**
0070:             * Bar chart with the bars for each value in the series stacked upon one another so that there is one bar for each x value
0071:             */
0072:            public final static int STACKEDBARCHART = 1;
0073:
0074:            /**
0075:             * A simple line chart with connected points
0076:             */
0077:            public final static int LINECHART = 2;
0078:
0079:            /**
0080:             * A pie chart showing the first series
0081:             */
0082:            public final static int PIECHART = 3;
0083:
0084:            /**
0085:             * A plot with points for each x-y value pair
0086:             */
0087:            public final static int SCATTERCHART = 4;
0088:
0089:            /**
0090:             * Linear axis
0091:             */
0092:            public static final int LINEAR = 0;
0093:
0094:            /**
0095:             * A Log10 axis
0096:             */
0097:            public static final int LOGARITHMIC = 1;
0098:
0099:            /**
0100:             * The series column/field in the source data table
0101:             */
0102:            public static final int SERIES = 0;
0103:
0104:            /**
0105:             * The labels column/field in the source data table
0106:             */
0107:            public static final int LABELS = 1;
0108:
0109:            /**
0110:             * The values column/field in the source data table
0111:             */
0112:            public static final int VALUES = 2;
0113:
0114:            /**
0115:             * The owner project and the context in which this object operates.
0116:             */
0117:            protected XProject currentProject = XProjectManager
0118:                    .getCurrentProject();
0119:
0120:            //----------------------------------------------------------------------------
0121:            private FontMetrics fm;
0122:            private String title, xTitle, yTitle;
0123:            private int xAxisType, yAxisType;
0124:
0125:            private double top;
0126:            private double bottom;
0127:            private double left;
0128:            private double right;
0129:            private double titleHeight;
0130:            private double labelWidth, labelHeight;
0131:            private double padding;
0132:            private double minX;
0133:            private double maxX;
0134:            private double minY;
0135:            private double maxY;
0136:            private double intervalX, intervalY;
0137:            private double minYAllowable;
0138:
0139:            private int numSeries, newSeries;
0140:            private double[] points, newPoints;
0141:            private double[] pairTotals;
0142:            private String[] seriesNames;
0143:
0144:            private Timer animationTimer;
0145:            private double animationScale = 1.0;
0146:            private double animationScaleInc = 0.1;
0147:            private JComponent chartPanel;
0148:            private JLayeredPane layeredPane;
0149:            private DecimalFormat xFormatter, yFormatter;
0150:            private boolean animateYAxis = true/*false*/;
0151:
0152:            private Image backgroundImage;
0153:
0154:            private double min;
0155:            private double max;
0156:            private int numCols;
0157:            private int numRecords;
0158:            private int currentViewId;
0159:            private XModel model;
0160:            private int mode = 0;
0161:
0162:            private double position = 0;
0163:            private double increment = 2;
0164:            private Vector cols;
0165:
0166:            private boolean drawBorder;
0167:            private boolean drawPoints;
0168:            private boolean labelPoints;
0169:            private int legendPos;
0170:
0171:            private double legendWidth;
0172:            private double legendHeight;
0173:            private double scale;
0174:
0175:            private String maxSeriesName;
0176:            private String maxXLabelName;
0177:            private String maxYLabelName;
0178:
0179:            /**
0180:             * An aray of areas that can be highlighted and selected
0181:             */
0182:            private ArrayList selectableAreas;
0183:            private SelectableArea highlightedArea;
0184:            private boolean highlightSelection;
0185:
0186:            /**
0187:             * The labels for the data-axis
0188:             */
0189:            private String[] xLabels;
0190:
0191:            /**
0192:             * The offset of the model field contain the labels if any
0193:             */
0194:            private int labelFields;
0195:
0196:            /**
0197:             * The offset of the model field containing the series values
0198:             */
0199:            private int seriesFields;
0200:
0201:            /**
0202:             * The offset of the field containing the value.
0203:             */
0204:            private int valueFields;
0205:
0206:            //----------------------------------------------------------------------------
0207:
0208:            /**
0209:             * Construct a new graph/chart
0210:             */
0211:            public XGraph() {
0212:                padding = 10;
0213:                minYAllowable = Double.NEGATIVE_INFINITY;
0214:                drawBorder = false;
0215:                drawPoints = false;
0216:                labelPoints = false;
0217:                labelFields = -1;
0218:                seriesFields = -1;
0219:                valueFields = 1;
0220:
0221:                legendPos = 0;
0222:                legendWidth = 130;
0223:                legendHeight = 85;
0224:                animationTimer = new Timer(1, this );
0225:                animationTimer.setCoalesce(true);
0226:                animationTimer.setRepeats(true);
0227:
0228:                layeredPane = new JLayeredPane();
0229:                chartPanel = new ChartPanel();
0230:                chartPanel.setOpaque(false);
0231:                layeredPane.add(chartPanel, new Integer(0));
0232:
0233:                setLayout(new BorderLayout());
0234:                add(layeredPane, BorderLayout.CENTER);
0235:                layeredPane.setVisible(true);
0236:                xFormatter = new DecimalFormat();
0237:                yFormatter = new DecimalFormat();
0238:                yFormatter.setMaximumFractionDigits(0);
0239:
0240:                selectableAreas = new ArrayList();
0241:                highlightSelection = false;
0242:            }
0243:
0244:            /**
0245:             * Define the location of the data in the model
0246:             * @param attribute the attribute being defined (LABELS, SERIES, VALUES)
0247:             * @param value the column of field where that data can be found
0248:             */
0249:            public void setModelStructure(int attribute, int value) {
0250:                if (attribute == LABELS)
0251:                    labelFields = value;
0252:                else if (attribute == SERIES)
0253:                    seriesFields = value;
0254:                else if (attribute == VALUES)
0255:                    valueFields = value;
0256:            }
0257:
0258:            /**
0259:             * Get the selectable area
0260:             * @return the selected area or null if there is no selection
0261:             */
0262:            public SelectableArea getSelectableArea() {
0263:                return highlightedArea;
0264:            }
0265:
0266:            /**
0267:             * An action handler for the animation timer
0268:             * @param e the event
0269:             */
0270:            public void actionPerformed(ActionEvent e) {
0271:                chartPanel.repaint();
0272:                if (animationScale > 0.0)
0273:                    animationScale -= animationScaleInc;
0274:            }
0275:
0276:            /**
0277:             * Set the bounds for this component and layout the children
0278:             * @param x the x coordinate
0279:             * @param y the y coordinate
0280:             * @param w the width
0281:             * @param h the height
0282:             */
0283:            public void setBounds(int x, int y, int w, int h) {
0284:                super .setBounds(x, y, w, h);
0285:                chartPanel.setBounds(0, 0, w, h);
0286:            }
0287:
0288:            /**
0289:             * Set the XScale options
0290:             * @param min the min value
0291:             * @param max the max value
0292:             * @param type the type of axis
0293:             * @param title the axis title
0294:             * @param labels the labels for the markers in the series
0295:             */
0296:            public void setXScale(double min, double max, int type,
0297:                    String title, String[] labels) {
0298:                minX = min;
0299:                maxX = max;
0300:                xTitle = title;
0301:                xAxisType = type;
0302:                xLabels = labels;
0303:            }
0304:
0305:            /**
0306:             * Set the XScale options
0307:             * @param type the type of axis
0308:             * @param title the axis title
0309:             */
0310:            public void setYScale(int type, String title) {
0311:                yTitle = title;
0312:                yAxisType = type;
0313:            }
0314:
0315:            /**
0316:             * Force a repaint
0317:             */
0318:            public void update() {
0319:                setModel(model);
0320:            }
0321:
0322:            /**
0323:             * Set the XModel which we will be generating the table from
0324:             * @param xmodel The XModel of data
0325:             */
0326:            public void setModel(XModel xmodel) {
0327:                model = xmodel;
0328:                XTranslator translator = currentProject.getTranslator();
0329:                if (model != null) {
0330:                    model.get();
0331:                    int numRows = model.getNumChildren();
0332:
0333:                    // Lookup the labels
0334:                    int idx = 0;
0335:                    Hashtable labels = new Hashtable();
0336:                    Hashtable series = new Hashtable();
0337:                    int labelCount = 0;
0338:                    int seriesCount = 0;
0339:                    for (int i = 0; i < numRows; i++) {
0340:                        XModel rowModel = (XModel) model.get(i);
0341:                        if (labelFields >= 0) {
0342:                            String labelValue = (String) rowModel
0343:                                    .getAttribValue(labelFields);
0344:                            labelValue = translator.translate(labelValue);
0345:                            if (labels.get(labelValue) == null)
0346:                                labels.put(labelValue,
0347:                                        new Integer(labelCount++));
0348:                        }
0349:                        if (seriesFields >= 0) {
0350:                            String seriesValue = (String) rowModel
0351:                                    .getAttribValue(seriesFields);
0352:                            seriesValue = translator.translate(seriesValue);
0353:                            if (series.get(seriesValue) == null)
0354:                                series.put(seriesValue, new Integer(
0355:                                        seriesCount++));
0356:                        }
0357:                    }
0358:                    xLabels = labelCount > 0 ? new String[labelCount] : null;
0359:                    Enumeration labelEnum = labels.keys();
0360:                    while (labelEnum.hasMoreElements()) {
0361:                        String text = (String) labelEnum.nextElement();
0362:                        xLabels[((Integer) labels.get(text)).intValue()] = text;
0363:                    }
0364:
0365:                    if (seriesCount > 0)
0366:                        numSeries = seriesCount;
0367:                    else
0368:                        numSeries = model.getNumAttributes() - 1
0369:                                - (labelCount > 0 ? 1 : 0);
0370:
0371:                    String names[] = new String[numSeries];
0372:                    Enumeration seriesEnum = series.keys();
0373:                    while (seriesEnum.hasMoreElements()) {
0374:                        String text = (String) seriesEnum.nextElement();
0375:                        names[((Integer) series.get(text)).intValue()] = text;
0376:                    }
0377:
0378:                    int seriesPts = (seriesCount > 0) ? (numSeries * 2 * labelCount)
0379:                            : (numRows * 2 * numSeries);
0380:                    double pts[] = new double[seriesPts];
0381:                    for (int i = 0; i < seriesPts; i++)
0382:                        pts[i] = Double.NaN;
0383:
0384:                    int seriesIdx, xIdx;
0385:                    String fieldValue;
0386:                    if (seriesFields >= 0) {
0387:                        // The series names/identifiers are contained in a singel column
0388:                        for (int i = 0; i < numRows; i++) {
0389:                            XModel rowModel = (XModel) model.get(i);
0390:
0391:                            fieldValue = (String) rowModel
0392:                                    .getAttribValue(seriesFields);
0393:                            fieldValue = translator.translate(fieldValue);
0394:                            seriesIdx = ((Integer) series.get(fieldValue))
0395:                                    .intValue();
0396:
0397:                            fieldValue = (String) rowModel
0398:                                    .getAttribValue(labelFields);
0399:                            fieldValue = translator.translate(fieldValue);
0400:                            xIdx = (labelFields >= 0 ? ((Integer) labels
0401:                                    .get(fieldValue)).intValue()
0402:                                    : (i % numSeries));
0403:
0404:                            String valueStr = (String) rowModel
0405:                                    .getAttribValue(valueFields);
0406:
0407:                            pts[(xIdx * 2) + (seriesIdx * labelCount * 2)] = (labelFields >= 0 ? xIdx
0408:                                    : Double.parseDouble(fieldValue));
0409:                            pts[(xIdx * 2) + (seriesIdx * labelCount * 2) + 1] = Double
0410:                                    .parseDouble(valueStr);
0411:                        }
0412:                    } else {
0413:                        // The series are contained in individual fields
0414:                        for (int j = 0; j < numSeries; j++) {
0415:                            names[j] = model.getAttribName(j + 1);
0416:                            for (int i = 0; i < numRows; i++) {
0417:                                XModel rowModel = (XModel) model.get(i);
0418:
0419:                                String s = rowModel.getAttribName(0);
0420:                                if (labelCount > 0)
0421:                                    pts[idx++] = s.length() > 0 ? Double
0422:                                            .parseDouble(s) : Double.NaN;
0423:                                else
0424:                                    pts[idx++] = i;
0425:                                s = (String) rowModel.getAttribValue(rowModel
0426:                                        .getAttribute(names[j]));
0427:                                pts[idx++] = s.length() > 0 ? Double
0428:                                        .parseDouble(s) : Double.NaN;
0429:                            }
0430:                        }
0431:                    }
0432:                    setData(pts, numSeries, names, true);
0433:                }
0434:            }
0435:
0436:            /**
0437:             * Set the chart data
0438:             * @param nSeries the number of series
0439:             * @param pts the data points in x, y pairs, a complete series at a time e.g. ax1, ay1, ax2, ay2, .... xn, yn, bx1, by1, bx2, by2
0440:             * @param names the names of the series
0441:             * @param animate true to animate new data
0442:             */
0443:            public void setData(double[] pts, int nSeries, String[] names,
0444:                    boolean animate) {
0445:                backgroundImage = null;
0446:                boolean showNow = !animate || (newPoints == null);
0447:                newSeries = nSeries;
0448:                newPoints = pts;
0449:                seriesNames = names;
0450:                if (showNow)
0451:                    showNewData();
0452:                else {
0453:                    animationScale = 0.1;
0454:                    animationScaleInc = -0.1;
0455:                    animationTimer.start();
0456:                }
0457:            }
0458:
0459:            /**
0460:             * Turn on/off the animation of new data
0461:             */
0462:            public void toggleYAxisAnimation() {
0463:                animateYAxis = !animateYAxis;
0464:            }
0465:
0466:            private void showNewData() {
0467:                minX = Double.MAX_VALUE;
0468:                maxX = Double.MIN_VALUE;
0469:                minY = Double.MAX_VALUE;
0470:                maxY = Double.MIN_VALUE;
0471:
0472:                numSeries = newSeries;
0473:
0474:                int numPoints = newPoints.length;
0475:                int numPairs = newPoints.length / (numSeries * 2);
0476:                if (mode == STACKEDBARCHART)
0477:                    pairTotals = new double[numPairs];
0478:
0479:                for (int i = 0; i < numPoints; i += 2) {
0480:                    double xValue = newPoints[i];
0481:                    double yValue = newPoints[i + 1];
0482:                    if (Double.isNaN(xValue) || Double.isNaN(yValue))
0483:                        continue;
0484:                    minX = Math.min(minX, xValue);
0485:                    maxX = Math.max(maxX, xValue);
0486:                    if (yValue > 0.0) {
0487:                        minY = Math.min(minY, yValue);
0488:                        maxY = Math.max(maxY, yValue);
0489:                    }
0490:                    if (mode == STACKEDBARCHART) {
0491:                        int idx = ((i / 2) % numPairs);
0492:                        pairTotals[idx] += yValue;
0493:                        maxY = Math.max(maxY, pairTotals[idx]);
0494:                    }
0495:                }
0496:
0497:                // X Interval --------------------------------------------------------------
0498:                double range = maxX - minX;
0499:                if (xLabels == null) {
0500:                    intervalX = 1000.0;
0501:                    if (range < intervalX) {
0502:                        intervalX = 100.0;
0503:                        if (range < intervalX)
0504:                            intervalX = 10.0;
0505:                        if (range < intervalX)
0506:                            intervalX = 1.0;
0507:                        if (range < intervalX)
0508:                            intervalX = 0.1;
0509:                    }
0510:                    range += intervalX;
0511:                    range += range % intervalX;
0512:
0513:                    if (range < 1.0) {
0514:                        xFormatter.setMinimumFractionDigits(2);
0515:                        xFormatter.setMaximumFractionDigits(2);
0516:                    } else if (range < 10.0) {
0517:                        xFormatter.setMinimumFractionDigits(1);
0518:                        xFormatter.setMaximumFractionDigits(1);
0519:                    } else {
0520:                        xFormatter.setMinimumFractionDigits(0);
0521:                        xFormatter.setMaximumFractionDigits(0);
0522:                    }
0523:
0524:                    minX -= Math.abs(minX) % intervalX;
0525:                    maxX = maxX + intervalX - Math.abs(maxX % intervalX);
0526:                } else {
0527:                    intervalX = range / xLabels.length;
0528:                    maxX++;
0529:                }
0530:                // -------------------------------------------------------------------------
0531:
0532:                // Y Interval --------------------------------------------------------------
0533:                range = maxY - minY;
0534:                intervalY = 1000.0;
0535:                if (range < intervalY) {
0536:                    intervalY = 100.0;
0537:                    if (range < intervalY)
0538:                        intervalY = 10.0;
0539:                    if (range < intervalY)
0540:                        intervalY = 1.0;
0541:                    if (range < intervalY)
0542:                        intervalY = 0.1;
0543:                }
0544:                range += intervalY;
0545:                range -= range % intervalY;
0546:                if (range < 1.0) {
0547:                    yFormatter.setMinimumFractionDigits(2);
0548:                    yFormatter.setMaximumFractionDigits(2);
0549:                } else if (range < 10.0) {
0550:                    yFormatter.setMinimumFractionDigits(1);
0551:                    yFormatter.setMaximumFractionDigits(1);
0552:                } else {
0553:                    yFormatter.setMinimumFractionDigits(0);
0554:                    yFormatter.setMaximumFractionDigits(0);
0555:                }
0556:                minY -= Math.abs(minY) % intervalY;
0557:                maxY = maxY + intervalY - Math.abs(maxY % intervalY);
0558:                // -------------------------------------------------------------------------
0559:
0560:                points = (double[]) newPoints.clone();
0561:
0562:                // - Size the legens and axis labels ---------------------------------------
0563:                maxSeriesName = "SERIES FOO";
0564:                if (seriesNames != null) {
0565:                    for (int i = 0; i < numSeries; i++) {
0566:                        if (seriesNames[i].length() > maxSeriesName.length())
0567:                            maxSeriesName = seriesNames[i];
0568:                    }
0569:                } else
0570:                    legendWidth = 130;
0571:                // -------------------------------------------------------------------------
0572:                maxXLabelName = xFormatter.format(maxX);
0573:                if (xLabels != null) {
0574:                    for (int i = 0; i < xLabels.length; i++)
0575:                        if (xLabels[i].length() > maxXLabelName.length())
0576:                            maxXLabelName = xLabels[i];
0577:                }
0578:                maxYLabelName = yFormatter.format(maxY);
0579:
0580:                animationScale = 1.0;
0581:                animationScaleInc = 0.1;
0582:                animationTimer.start();
0583:            }
0584:
0585:            /**
0586:             * Setup the envelope component for printing
0587:             * @param g the graphics context
0588:             */
0589:            public void print(Graphics g) {
0590:                animationScale = 0.0;
0591:                super .print(g);
0592:            }
0593:
0594:            /**
0595:             * Renders the chart image.
0596:             * @param g the graphics context
0597:             */
0598:            public void paintComponent(Graphics g) {
0599:                Dimension size = getSize();
0600:                int width = size.width;
0601:                int height = size.height;
0602:
0603:                if ((backgroundImage == null) && (maxSeriesName != null)) {
0604:                    Graphics2D g2d = (Graphics2D) g;
0605:                    backgroundImage = createImage(width, height);
0606:                    if (backgroundImage != null)
0607:                        g2d = (Graphics2D) backgroundImage.getGraphics();
0608:
0609:                    g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
0610:                            RenderingHints.VALUE_RENDER_QUALITY);
0611:
0612:                    g2d.setColor(getBackground());
0613:                    g2d.fillRect(0, 0, getSize().width, getSize().height);
0614:                    g2d.setColor(getForeground());
0615:
0616:                    // draw the title
0617:                    if (fm == null)
0618:                        fm = getFontMetrics(getFont());
0619:
0620:                    if (maxSeriesName == null)
0621:                        legendWidth = 100;
0622:                    else
0623:                        legendWidth = 40.0
0624:                                + padding
0625:                                + fm.getStringBounds(maxSeriesName, g)
0626:                                        .getWidth();
0627:                    legendHeight = padding + numSeries * fm.getHeight();
0628:
0629:                    labelHeight = fm.getHeight();
0630:                    labelWidth = Math.max(fm
0631:                            .stringWidth(new Integer((int) maxX).toString()),
0632:                            fm.stringWidth(new Integer((int) maxY).toString())) + 2;
0633:                    titleHeight = labelHeight * 2;
0634:                    top = padding + titleHeight;
0635:                    bottom = getSize().height - padding - getXLabelHeight(g)
0636:                            - getLegendSpace(true);
0637:                    if (xTitle != null)
0638:                        bottom -= padding + labelHeight;
0639:                    left = padding + labelWidth;
0640:                    if (yTitle != null)
0641:                        left += padding + labelWidth;
0642:                    right = getSize().width - padding - getLegendSpace(false);
0643:
0644:                    Font fnt = g.getFont();
0645:                    Font tFont = new Font("Helv", Font.BOLD, 24);
0646:                    FontMetrics fmt = getFontMetrics(tFont);
0647:                    g2d.setFont(tFont);
0648:                    if ((title != null) && (title.length() > 0)) {
0649:                        drawString(g2d, title,
0650:                                (width - fmt.stringWidth(title)) / 2, top);
0651:                        top += padding;
0652:                    }
0653:                    g2d.setFont(fnt);
0654:
0655:                    switch (mode) {
0656:                    case PIECHART:
0657:                        break;
0658:
0659:                    case LINECHART:
0660:                    default:
0661:                        drawScale(g2d, false);
0662:                        drawScale(g2d, true);
0663:                        break;
0664:                    }
0665:
0666:                    drawLegend(g2d, getLegendX(left, right), getLegendY(top,
0667:                            bottom), (legendPos == 5 ? right - left
0668:                            : legendWidth),
0669:                            (legendPos == 5 ? getLegendSpace(true)
0670:                                    : legendHeight));
0671:
0672:                    if (backgroundImage != null)
0673:                        g2d.dispose();
0674:                }
0675:
0676:                if (backgroundImage != null)
0677:                    g.drawImage(backgroundImage, 0, 0, width, height, null);
0678:
0679:                if (drawBorder) {
0680:                    g.setColor(Color.gray);
0681:                    g.drawRect(0, 0, getSize().width - 1, getSize().height - 1);
0682:                }
0683:            }
0684:
0685:            private double scaleX(double x) {
0686:                return left + (((x - minX) * (right - left)) / (maxX - minX));
0687:            }
0688:
0689:            private double scaleY(double y) {
0690:                return Math
0691:                        .min(
0692:                                bottom,
0693:                                bottom
0694:                                        - ((scale * (y - minY) * (bottom - top)) / (maxY - minY)));
0695:            }
0696:
0697:            /**
0698:             * Draw a scale
0699:             * @param g2d the graphics context
0700:             * @param isVertical true to draw a vertical axis, otherwise the horizontal axis is drawn
0701:             */
0702:            public void drawScale(Graphics2D g2d, boolean isVertical) {
0703:                DecimalFormat formatter = isVertical ? yFormatter : xFormatter;
0704:
0705:                AffineTransform identityTransform = g2d.getTransform();
0706:                Color frgd = getForeground();
0707:
0708:                //double rightExtent = right - ( legendPos == 5 ? getLegendSpace( true ) : legendHeight );
0709:                double min = isVertical ? minY : minX;
0710:                double max = isVertical ? maxY : maxX;
0711:                double count = (int) ((max - min) / (isVertical ? intervalY
0712:                        : intervalX));
0713:                if (!isVertical && (xLabels != null))
0714:                    count = xLabels.length;
0715:                double separation = (max - min) / count;
0716:                double xLabelSpace = getXLabelHeight(g2d);
0717:                double deltaY = (bottom - top) / count;
0718:                double deltaX = (right - left) / count;
0719:
0720:                g2d.setColor(frgd);
0721:                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0722:                        RenderingHints.VALUE_ANTIALIAS_OFF);
0723:                g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
0724:                        RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
0725:                AffineTransform at = ((AffineTransform) identityTransform
0726:                        .clone());
0727:                if (isVertical) {
0728:                    at.translate(
0729:                            left
0730:                                    - padding
0731:                                    / 2
0732:                                    - fm.stringWidth(formatter
0733:                                            .format(new Double(max))), top);
0734:                } else {
0735:                    at.translate(
0736:                            left - ((xLabels == null) ? 0.0 : xLabelSpace),
0737:                            bottom + xLabelSpace);
0738:                    at.rotate(-Math.PI / 4);
0739:                }
0740:
0741:                float xInc = (float) (isVertical ? 0.0 : deltaX);
0742:                float yInc = (float) (isVertical ? deltaY : 0.0);
0743:                g2d.setTransform(at);
0744:                if (isVertical || (xLabels == null))
0745:                    g2d.drawString(formatter.format(new Double(isVertical ? max
0746:                            : min)), 0.0F, 0.0F);
0747:                for (int i = 0; i < count; i++) {
0748:                    if (isVertical) {
0749:                        at.translate(xInc, yInc);
0750:                    } else {
0751:                        at.rotate(Math.PI / 4);
0752:                        at.translate(xInc, yInc);
0753:                        at.rotate(-Math.PI / 4);
0754:                    }
0755:
0756:                    g2d.setTransform(at);
0757:                    double labelValue;
0758:                    if (i == count)
0759:                        labelValue = isVertical ? min : max;
0760:                    else if (isVertical)
0761:                        labelValue = (max - (i + 1) * separation);
0762:                    else
0763:                        labelValue = (min + (i + 1) * separation);
0764:
0765:                    String label = "";
0766:                    if (!isVertical && (xLabels != null))
0767:                        label = xLabels[i];
0768:                    else
0769:                        label = formatter.format(new Double(labelValue));
0770:                    g2d.drawString(label,
0771:                            isVertical ? 0.0F : (float) (xLabelSpace
0772:                                    - fm.stringWidth(label) + 0.6 * fm
0773:                                    .getHeight()), 0.0F);
0774:                }
0775:
0776:                g2d.setTransform(identityTransform);
0777:
0778:                // draw the vertical and horizontal lines
0779:                if (isVertical)
0780:                    g2d.drawLine((int) left, (int) top, (int) left,
0781:                            (int) bottom);
0782:                else
0783:                    g2d.drawLine((int) left, (int) bottom, (int) right,
0784:                            (int) bottom);
0785:
0786:                g2d.setColor(frgd);
0787:                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0788:                        RenderingHints.VALUE_ANTIALIAS_ON);
0789:                g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
0790:                        RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
0791:                g2d.setColor(Color.lightGray);
0792:                Stroke oldStroke = g2d.getStroke();
0793:                g2d.setStroke(new BasicStroke(0.5F, BasicStroke.CAP_ROUND,
0794:                        BasicStroke.JOIN_ROUND, 2.0F, new float[] { 2.0F, 2.0F,
0795:                                4.0F, 2.0F }, 0.0F));
0796:                for (int i = 0; i <= count; i++) {
0797:                    if (isVertical)
0798:                        g2d.drawLine((int) left, (int) (top + deltaY * i),
0799:                                (int) right, (int) (top + deltaY * i));
0800:                    else if (i == count)
0801:                        g2d.drawLine((int) (right - 1), (int) top,
0802:                                (int) (right - 1), (int) bottom);
0803:                    else
0804:                        g2d.drawLine((int) (left + (i + 1) * deltaX),
0805:                                (int) top, (int) (left + (i + 1) * deltaX),
0806:                                (int) bottom);
0807:                }
0808:                g2d.setColor(frgd);
0809:                g2d.setStroke(oldStroke);
0810:
0811:                // Draw the labels
0812:                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0813:                        RenderingHints.VALUE_ANTIALIAS_OFF);
0814:                g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
0815:                        RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
0816:
0817:                if ((isVertical) && (yTitle != null)) {
0818:                    AttributedString as = new AttributedString(yTitle);
0819:                    as.addAttribute(TextAttribute.FAMILY, "arial");
0820:                    as.addAttribute(TextAttribute.SIZE, new Float(
0821:                            (float) (10.0F)));
0822:                    as.addAttribute(TextAttribute.FOREGROUND, frgd);
0823:
0824:                    at = ((AffineTransform) identityTransform.clone());
0825:                    at.translate(labelHeight + padding, top
0826:                            + (bottom - top + fm.stringWidth(yTitle)) / 2);
0827:                    at.rotate(-Math.PI / 2);
0828:                    g2d.setTransform(at);
0829:
0830:                    g2d.drawString(as.getIterator(), 0.0F, 0.0F);
0831:                } else if (xTitle != null) {
0832:                    AttributedString as = new AttributedString(xTitle);
0833:                    as.addAttribute(TextAttribute.FAMILY, "arial");
0834:                    as.addAttribute(TextAttribute.SIZE, new Float(
0835:                            (float) (10.0F)));
0836:                    as.addAttribute(TextAttribute.FOREGROUND, frgd);
0837:                    g2d.setTransform(identityTransform);
0838:                    g2d.drawString(as.getIterator(), (float) (left + (right
0839:                            - left - fm.stringWidth(xTitle)) / 2),
0840:                            (float) (bottom + xLabelSpace + padding + fm
0841:                                    .getHeight()));
0842:                }
0843:                g2d.setTransform(identityTransform);
0844:            }
0845:
0846:            /**
0847:             * Draw a simple bar chart
0848:             * @param g2d the graphics context
0849:             */
0850:            public void drawBarChart(Graphics2D g2d) {
0851:                g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
0852:                        RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
0853:                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0854:                        RenderingHints.VALUE_ANTIALIAS_ON);
0855:
0856:                int numPairs = points.length / (numSeries * 2);
0857:                double increment = (scaleX(maxX) - scaleX(minX))
0858:                        / (2.0 * numPairs * numSeries);
0859:                for (int i = 0; i < numSeries; i++) {
0860:                    Color seriesColor = getColor(i);
0861:                    g2d.setColor(seriesColor);
0862:
0863:                    double seriesInc = i * increment * 2.0;
0864:                    double yStart = points[i * numPairs * 2 + 1];
0865:                    yStart = scaleY(yStart);
0866:                    boolean curveStarted = false;
0867:                    for (int j = 0; j < numPairs; j++) {
0868:                        int offset = i * numPairs * 2 + j * 2;
0869:
0870:                        double xValue = points[offset];
0871:                        double yValue = points[offset + 1];
0872:
0873:                        double xScaled = scaleX(xValue);
0874:                        double yScaled = scaleY(yValue);
0875:
0876:                        if (!Double.isNaN(yValue) && (yValue > minYAllowable)) {
0877:                            double deltaX = 0.0;
0878:                            if (xLabels != null)
0879:                                deltaX = -increment;
0880:
0881:                            fill3DRect(g2d, xScaled + deltaX + seriesInc,
0882:                                    yScaled, 2 * increment, bottom - yScaled,
0883:                                    true);
0884:                            if (scale > 0.99)
0885:                                selectableAreas.add(new SelectableArea(
0886:                                        xLabels[j], seriesNames[i], yValue,
0887:                                        new Area(
0888:                                                new Rectangle2D.Double(xScaled
0889:                                                        + deltaX + seriesInc,
0890:                                                        yScaled, 2 * increment,
0891:                                                        bottom - yScaled)),
0892:                                        seriesColor));
0893:                        }
0894:
0895:                        if (labelPoints) {
0896:                            String valueStr = yFormatter.format(yValue);
0897:                            drawString(g2d, valueStr, xScaled
0898:                                    - fm.stringWidth(valueStr), yScaled + 4);
0899:                        }
0900:                    }
0901:                }
0902:            }
0903:
0904:            /**
0905:             * Draw a stacked bar chart
0906:             * @param g2d the graphics context
0907:             */
0908:            public void drawStackedBarChart(Graphics2D g2d) {
0909:                g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
0910:                        RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
0911:                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0912:                        RenderingHints.VALUE_ANTIALIAS_ON);
0913:
0914:                int numPairs = points.length / (numSeries * 2);
0915:                double increment = (scaleX(maxX) - scaleX(minX))
0916:                        / (2.0 * numPairs);
0917:                for (int j = 0; j < numPairs; j++) {
0918:                    double yStart = 0.0;
0919:                    boolean curveStarted = false;
0920:                    for (int i = 0; i < numSeries; i++) {
0921:                        int offset = i * numPairs * 2 + j * 2;
0922:                        double xValue = points[offset];
0923:                        double yValue = points[offset + 1];
0924:                        if (Double.isNaN(yValue) || (yValue < minYAllowable))
0925:                            continue;
0926:
0927:                        Color seriesColor = getGeneratedColor(i);
0928:                        g2d.setColor(seriesColor);
0929:                        double xScaled = scaleX(xValue);
0930:                        double yScaled = bottom - scaleY(yValue);
0931:
0932:                        fill3DRect(g2d, xScaled + 2.0, bottom - yStart
0933:                                - yScaled, 2.0 * increment - 4.0, yScaled, true);
0934:
0935:                        if (scale > 0.99)
0936:                            selectableAreas.add(new SelectableArea(xLabels[j],
0937:                                    seriesNames[i], yValue, new Area(
0938:                                            new Rectangle2D.Double(
0939:                                                    xScaled + 2.0, bottom
0940:                                                            - yStart - yScaled,
0941:                                                    2.0 * increment - 4.0,
0942:                                                    yScaled)), seriesColor));
0943:
0944:                        if (labelPoints) {
0945:                            String valueStr = yFormatter.format(yValue);
0946:                            drawString(g2d, valueStr, xScaled
0947:                                    - fm.stringWidth(valueStr), yScaled + 4);
0948:                        }
0949:                        yStart += yScaled;
0950:                    }
0951:                }
0952:            }
0953:
0954:            /**
0955:             * Draw a line chart
0956:             * @param g2d the graphics context
0957:             */
0958:            public void drawLineChart(Graphics2D g2d) {
0959:                g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
0960:                        RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
0961:                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0962:                        RenderingHints.VALUE_ANTIALIAS_ON);
0963:
0964:                int numPairs = points.length / (numSeries * 2);
0965:                double previousX = 0.0;
0966:                double previousY = 0.0;
0967:                for (int i = 0; i < numSeries; i++) {
0968:                    Color seriesColor = getColor(i);
0969:                    g2d.setColor(seriesColor);
0970:
0971:                    double yStart = points[i * numPairs * 2 + 1];
0972:                    yStart = scaleY(yStart);
0973:                    GeneralPath gp = new GeneralPath();
0974:                    gp.moveTo((float) left, (float) yStart);
0975:                    boolean curveStarted = false;
0976:                    for (int j = 0; j < numPairs; j++) {
0977:                        int offset = i * numPairs * 2 + j * 2;
0978:
0979:                        double xValue = points[offset];
0980:                        double yValue = points[offset + 1];
0981:
0982:                        double xScaled = scaleX(xValue);
0983:                        double yScaled = scaleY(yValue);
0984:
0985:                        if (curveStarted) {
0986:                            if (!Double.isNaN(yValue)
0987:                                    && (yValue > minYAllowable))
0988:                                gp.lineTo((float) xScaled, (float) yScaled);
0989:                        } else {
0990:                            previousY = yScaled;
0991:                            gp.moveTo((float) xScaled, (float) yScaled);
0992:                            if (!Double.isNaN(yValue)
0993:                                    && (yValue > minYAllowable))
0994:                                curveStarted = true;
0995:                        }
0996:                        previousX = xScaled;
0997:
0998:                        if (labelPoints) {
0999:                            String valueStr = yFormatter.format(yValue);
1000:                            drawString(g2d, valueStr, xScaled
1001:                                    - fm.stringWidth(valueStr), yScaled + 4);
1002:                        }
1003:                        if (drawPoints)
1004:                            fillOval(g2d, xScaled - 2, yScaled - 2, 4, 4);
1005:                        if ((scale > 0.99) && !Double.isNaN(yValue)
1006:                                && (yValue > minYAllowable)) {
1007:                            selectableAreas.add(new SelectableArea(xLabels[j],
1008:                                    seriesNames[i], yValue, new Area(
1009:                                            new Ellipse2D.Double(xScaled - 3,
1010:                                                    yScaled - 3, 6, 6)),
1011:                                    seriesColor));
1012:                            previousY = yScaled;
1013:                        }
1014:                    }
1015:                    if (mode == LINECHART)
1016:                        g2d.draw(gp);
1017:                }
1018:            }
1019:
1020:            /**
1021:             * A helper method to take care of type conversions
1022:             */
1023:            private void drawLine(Graphics2D g2d, double x, double y, double w,
1024:                    double h) {
1025:                g2d.drawLine((int) x, (int) y, (int) w, (int) h);
1026:            }
1027:
1028:            /**
1029:             * A helper method to take care of type conversions
1030:             */
1031:            private void fillRect(Graphics2D g2d, double x, double y, double w,
1032:                    double h) {
1033:                g2d.fillRect((int) x, (int) y, (int) w, (int) h);
1034:            }
1035:
1036:            /**
1037:             * A helper method to take care of type conversions
1038:             */
1039:            private void fill3DRect(Graphics2D g2d, double x, double y,
1040:                    double w, double h, boolean raised) {
1041:                g2d.fill3DRect((int) (x + 0.5), (int) (y + 0.5),
1042:                        (int) (w + 0.5), (int) (h + 0.5), raised);
1043:            }
1044:
1045:            /**
1046:             * A helper method to take care of type conversions
1047:             */
1048:            private void drawRect(Graphics2D g2d, double x, double y, double w,
1049:                    double h) {
1050:                g2d.drawRect((int) x, (int) y, (int) w, (int) h);
1051:            }
1052:
1053:            /**
1054:             * A helper method to take care of type conversions
1055:             */
1056:            private void fillOval(Graphics2D g2d, double x, double y,
1057:                    double r1, double r2) {
1058:                g2d.fillOval((int) x, (int) y, (int) r1, (int) r2);
1059:            }
1060:
1061:            /**
1062:             * A helper method to take care of type conversions
1063:             */
1064:            private void drawString(Graphics2D g2d, String s, double x, double y) {
1065:                g2d.drawString(s, (int) x, (int) y);
1066:            }
1067:
1068:            /**
1069:             * A helper method to take care of type conversions
1070:             */
1071:            private void fillArc(Graphics2D g2d, double arcX, double arcY,
1072:                    double sizeX, double sizeY, double startAngle,
1073:                    double endAngle) {
1074:                g2d.fillArc((int) arcX, (int) arcY, (int) sizeX, (int) sizeY,
1075:                        (int) startAngle, (int) endAngle);
1076:            }
1077:
1078:            /**
1079:             * Get the height of the X axis labels
1080:             * @return the label space height
1081:             * @param g the graphics context
1082:             */
1083:            protected double getXLabelHeight(Graphics g) {
1084:                return fm.getStringBounds(maxXLabelName, g).getWidth() * 0.6;
1085:            }
1086:
1087:            /**
1088:             * Get the height of the X axis labels
1089:             * @return the label space height
1090:             * @param g the graphics context
1091:             */
1092:            protected double getYLabelWidth(Graphics g) {
1093:                return fm.getStringBounds(maxYLabelName, g).getWidth();
1094:            }
1095:
1096:            /**
1097:             * Get the space consumed by the legend
1098:             * @param verticalSpace true if the legend is shown horizontally below the chart
1099:             * @return the size the legend consumes if it does not overlap the chart area
1100:             */
1101:            protected int getLegendSpace(boolean verticalSpace) {
1102:                if (legendPos < 4)
1103:                    return 0;
1104:                else if (legendPos == 4)
1105:                    return (verticalSpace ? 0 : (int) (legendWidth + padding));
1106:                else
1107:                    return (verticalSpace ? (int) (fm.getHeight() + padding)
1108:                            : 0);
1109:            }
1110:
1111:            /**
1112:             * Get the legend x coordinate
1113:             * @param left the left edge of the chart 
1114:             * @param right the right edge of the chart 
1115:             * @return the starting x coordinate of the legend
1116:             */
1117:            protected int getLegendX(double left, double right) {
1118:                double pos = left + 10.0;
1119:                switch (legendPos) {
1120:                case 0:
1121:                    break;
1122:                case 1:
1123:                    pos = right - 10 - legendWidth;
1124:                    break;
1125:                case 2:
1126:                    break;
1127:                case 3:
1128:                    pos = right - 10 - legendWidth;
1129:                    break;
1130:                case 4:
1131:                    pos = right + 10;
1132:                    break;
1133:                case 5:
1134:                    pos = left;
1135:                default:
1136:                    break;
1137:                }
1138:
1139:                return (int) pos;
1140:            }
1141:
1142:            /**
1143:             * Get the legend y coordinate
1144:             * @param top the top edge of the chart
1145:             * @param bottom the bottom edge of the chart
1146:             * @return the starting y coordinate of the legend
1147:             */
1148:            protected int getLegendY(double top, double bottom) {
1149:                double pos = top + 10.0;
1150:                switch (legendPos) {
1151:                case 0:
1152:                    break;
1153:                case 1:
1154:                    break;
1155:                case 2:
1156:                    pos = bottom - 10 - legendHeight;
1157:                    break;
1158:                case 3:
1159:                    pos = bottom - 10 - legendHeight;
1160:                    break;
1161:                case 4:
1162:                    pos = top;
1163:                    break;
1164:                case 5:
1165:                    pos = bottom + 2 * padding + fm.getHeight();
1166:                    break;
1167:                default:
1168:                    break;
1169:                }
1170:
1171:                return (int) pos;
1172:            }
1173:
1174:            /**
1175:             * Draw the chart legend
1176:             * @param g2d the graphics context
1177:             * @param x the x/left position
1178:             * @param y the y/top position
1179:             * @param w the width
1180:             * @param h the height
1181:             */
1182:            protected void drawLegend(Graphics2D g2d, double x, double y,
1183:                    double w, double h) {
1184:                g2d.setColor(getBackground());
1185:                fillRect(g2d, x, y, w, h);
1186:                g2d.setColor(Color.lightGray);
1187:                drawRect(g2d, x, y, w, h);
1188:
1189:                Stroke oldStroke = g2d.getStroke();
1190:                g2d.setStroke(new BasicStroke(0.5F));
1191:                double xpos = x;
1192:                double ypos = y;
1193:                for (int i = 0; i < numSeries; i++) {
1194:                    if (seriesNames[i] == null)
1195:                        continue;
1196:                    g2d.setColor((mode == STACKEDBARCHART) ? getGeneratedColor(
1197:                            i, 255) : getColor(i, 255));
1198:                    drawLine(g2d, xpos + 10, ypos + 10, xpos + 30, ypos + 10);
1199:                    g2d.setColor(getForeground());
1200:                    drawString(g2d, seriesNames[i], xpos + 40, ypos + 15);
1201:                    if (legendPos != 5)
1202:                        ypos += fm.getHeight();
1203:                    else
1204:                        xpos += 100;
1205:                }
1206:                g2d.setStroke(oldStroke);
1207:            }
1208:
1209:            /** 
1210:             * Get the drawBorder flag value
1211:             * @return true if the border is drawn
1212:             */
1213:            public boolean getDrawBorder() {
1214:                return drawBorder;
1215:            }
1216:
1217:            /** 
1218:             * Set the drawBorder flag value
1219:             * @param b true if the border is drawn
1220:             */
1221:            public void setDrawBorder(boolean b) {
1222:                drawBorder = b;
1223:            }
1224:
1225:            /** 
1226:             * Get the labels flag value
1227:             * @return true if the border is drawn
1228:             */
1229:            public boolean getLabels() {
1230:                return labelPoints;
1231:            }
1232:
1233:            /** 
1234:             * Set the labels flag value
1235:             * @param b true if the lables are drawn on the data points
1236:             */
1237:            public void setLabels(boolean b) {
1238:                labelPoints = b;
1239:            }
1240:
1241:            /** 
1242:             * Get the highlight flag value
1243:             * @return true if the current value is highlighted as the mouse is moved
1244:             */
1245:            public boolean getHighlight() {
1246:                return highlightSelection;
1247:            }
1248:
1249:            /** 
1250:             * Set the highlight flag value
1251:             * @param b true if the current value is to be highlighted as the mouse is moved
1252:             */
1253:            public void setHighlight(boolean b) {
1254:                highlightSelection = b;
1255:            }
1256:
1257:            /** 
1258:             * Get the markers flag value
1259:             * @return true if the markers are drwan for each point
1260:             */
1261:            public boolean getMarkers() {
1262:                return drawPoints;
1263:            }
1264:
1265:            /** 
1266:             * Set the markers flag value
1267:             * @param b true if the markers are drwan for each point
1268:             */
1269:            public void setMarkers(boolean b) {
1270:                drawPoints = b;
1271:            }
1272:
1273:            /** 
1274:             * Get the minimum Y value
1275:             * @return the minimum value
1276:             */
1277:            public double getMinY() {
1278:                return minY;
1279:            }
1280:
1281:            /** 
1282:             * Set the minimum Y value
1283:             * @param y the minimum value
1284:             */
1285:            public void setMinY(double y) {
1286:                minY = y;
1287:            }
1288:
1289:            /** 
1290:             * Get the maximum Y value
1291:             * @return the maximum value
1292:             */
1293:            public double getMaxY() {
1294:                return maxY;
1295:            }
1296:
1297:            /** 
1298:             * Set the maximum Y value
1299:             * @param y the maximum value
1300:             */
1301:            public void setMaxY(double y) {
1302:                maxY = y;
1303:            }
1304:
1305:            /** 
1306:             * Get the legend position
1307:             * @return the position value, 0=top left, 1=top right, 2=bottom left, 3=bottom right, 4=right margin, 5=bottom margin
1308:             */
1309:            public int getLegendPos() {
1310:                return legendPos;
1311:            }
1312:
1313:            /** 
1314:             * Set the legend position
1315:             * @param pos value=the legend position, 0=top left, 1=top right, 2=bottom left, 3=bottom right, 4=right margin, 5=bottom margin
1316:             */
1317:            public void setLegendPos(int y) {
1318:                legendPos = y;
1319:            }
1320:
1321:            /**
1322:             * Draw a pie chart with the first data series
1323:             * @param g2d the graphics context
1324:             */
1325:            public void drawPieChart(Graphics2D g2d) {
1326:                g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
1327:                        RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
1328:                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1329:                        RenderingHints.VALUE_ANTIALIAS_ON);
1330:
1331:                double height3d = (bottom - top - 2 * padding) / 10.0;
1332:                double size = Math.min(bottom - top, right - left) - padding
1333:                        - 30;
1334:                double sizeX = scale * (right - left) - padding - 30;
1335:                double sizeY = scale * (bottom - top) - padding - 30 - height3d;
1336:
1337:                double arcX = left + (right - left - sizeX) / 2;
1338:                double arcY = top + (bottom - top - sizeY - height3d) / 2;
1339:                Color temp = g2d.getColor();
1340:
1341:                double sumTot = 0;
1342:                Point.Double center = new Point.Double((right - left) / 2,
1343:                        (bottom - top - height3d) / 2);
1344:
1345:                int numPairs = points.length / (numSeries * 2);
1346:                double totalValue = 0.0;
1347:                for (int i = 0; i < 1/*numSeries*/; i++) {
1348:                    for (int j = 0; j < numPairs; j++) {
1349:                        int offset = i * numPairs * 2 + j * 2;
1350:                        double yValue = points[offset + 1];
1351:                        if (!Double.isNaN(yValue) && (yValue > minYAllowable))
1352:                            totalValue += yValue;
1353:                    }
1354:                }
1355:
1356:                int i = 0; // Only take the first series
1357:                double degrees = 0.0;
1358:                Area topCircle = new Area(new Arc2D.Double(arcX, arcY, sizeX,
1359:                        sizeY, 0.0, 360.0, Arc2D.PIE));
1360:                Area bottomCircle = new Area(new Arc2D.Double(arcX, arcY
1361:                        + height3d, sizeX, sizeY, 0.0, 360.0, Arc2D.PIE));
1362:                Area spacer = new Area(new Rectangle2D.Double(arcX, arcY
1363:                        + sizeY / 2.0, sizeX, height3d));
1364:                spacer.subtract(topCircle);
1365:                for (int j = 0; j < numPairs; j++) {
1366:                    int offset = i * numPairs * 2 + j * 2;
1367:                    double yValue = points[offset + 1];
1368:                    if (!Double.isNaN(yValue) && (yValue > minYAllowable)) {
1369:                        double adjustedValue = yValue * 360.0 / totalValue;
1370:                        Color pieColor = getColor(j);
1371:                        g2d.setColor(pieColor);
1372:                        Arc2D.Double arc = new Arc2D.Double(arcX, arcY, sizeX,
1373:                                sizeY, degrees, adjustedValue, Arc2D.PIE);
1374:                        g2d.fill(arc);
1375:
1376:                        if (scale > 0.99)
1377:                            selectableAreas.add(new SelectableArea(xLabels[j],
1378:                                    seriesNames[0], yValue, new Area(arc),
1379:                                    pieColor));
1380:
1381:                        Arc2D.Double arc2 = new Arc2D.Double(arcX, arcY, sizeX,
1382:                                sizeY, degrees, adjustedValue, Arc2D.OPEN);
1383:                        Rectangle2D rect = arc2.getBounds2D();
1384:                        double sideX = rect.getX();
1385:                        double sideW = rect.getWidth();
1386:                        Rectangle2D.Double rectangle = new Rectangle2D.Double(
1387:                                sideX, arcY + sizeY / 2.0, sideW, sizeY / 2.0
1388:                                        + height3d);
1389:                        Area area = new Area(rectangle);
1390:
1391:                        // The spacer has a left and a right wedge, so we need to find the 
1392:                        // intersection. We must also do this before subtracting the top and 
1393:                        // bottom circles.
1394:                        Area spacerArea = new Area(spacer);
1395:                        spacerArea.intersect(area);
1396:
1397:                        area.subtract(topCircle);
1398:                        area.intersect(bottomCircle);
1399:                        area.add(spacerArea);
1400:
1401:                        g2d.setColor(darken(pieColor, degrees));
1402:
1403:                        GradientPaint gradient = new GradientPaint(0.0F, 0.0F,
1404:                                darken(pieColor, degrees),
1405:                                (float) sideW * 2.0F, 0.0F, darken(pieColor,
1406:                                        degrees + adjustedValue), true);
1407:                        g2d.setPaint(gradient);
1408:                        g2d.fill(area);
1409:
1410:                        Point.Double titlePosition = new Point.Double(0, 0);
1411:                        titlePosition.x = (Math.cos(degrees * Math.PI / 180.0
1412:                                + adjustedValue * Math.PI / 360.0)
1413:                                * sizeX / 2.0)
1414:                                + center.x + left;
1415:                        titlePosition.y = center.y
1416:                                - (Math.sin(degrees * Math.PI / 180.0
1417:                                        + adjustedValue * Math.PI / 360.0)
1418:                                        * sizeY / 2.0) + top;
1419:                        String name = "";//df.getFieldName( ((Integer)cols.elementAt( i )).intValue());
1420:                        if (titlePosition.x <= center.x)
1421:                            titlePosition.x = titlePosition.x
1422:                                    - fm.stringWidth(name) - 1;
1423:                        else if (titlePosition.x > center.x)
1424:                            titlePosition.x += 1;
1425:
1426:                        if (titlePosition.y > center.y)
1427:                            titlePosition.y = titlePosition.y + fm.getHeight()
1428:                                    + 1;
1429:                        else if (titlePosition.y <= center.y)
1430:                            titlePosition.y -= 1;
1431:
1432:                        g2d.setColor(temp);
1433:                        drawString(g2d, name, titlePosition.x, titlePosition.y);
1434:                        degrees += adjustedValue;
1435:                    }
1436:                }
1437:            }
1438:
1439:            /**
1440:             * Highlight the selected area - if any
1441:             * @param g2d the graphics context
1442:             */
1443:            public void drawSelectedArea(Graphics2D g2d) {
1444:                if (highlightSelection && (highlightedArea != null)) {
1445:                    Stroke oldStroke = g2d.getStroke();
1446:                    g2d.setStroke(new BasicStroke(2.0F));
1447:                    g2d.setColor(highlightedArea.color.brighter());
1448:                    g2d.draw(highlightedArea.area);
1449:                    g2d.setStroke(oldStroke);
1450:                }
1451:            }
1452:
1453:            /**
1454:             * Get an index color with full opacity
1455:             * @param idx the color index
1456:             */
1457:            private Color getColor(int idx) {
1458:                int opacity = (int) (255 * (1.0 - Math.max(animationScale, 0.0)));
1459:                opacity = Math.max(0, Math.min(255, opacity));
1460:                return getColor(idx, opacity);
1461:            }
1462:
1463:            /**
1464:             * Get an index color with partial opacity
1465:             * @param idx the color index
1466:             * @param opacity the opacity value in the range 0-255
1467:             */
1468:            private Color getColor(int idx, int opacity) {
1469:                opacity = Math.min(255, Math.max(opacity, 0));
1470:                switch (idx % 18) {
1471:                case 0:
1472:                    return new Color(255, 0, 0, opacity);
1473:                case 1:
1474:                    return new Color(0, 0, 228, opacity);
1475:                case 2:
1476:                    return new Color(0, 228, 0, opacity);
1477:                case 3:
1478:                    return new Color(168, 0, 168, opacity);
1479:                case 4:
1480:                    return new Color(255, 208, 41, opacity);
1481:                case 5:
1482:                    return new Color(51, 102, 255, opacity); // Mid blue
1483:                case 6:
1484:                    return new Color(255, 255, 0, opacity); // yellow
1485:                case 7:
1486:                    return new Color(153, 153, 255, opacity); // Sun purple
1487:                case 8:
1488:                    return new Color(228, 204, 228, opacity); // Pink
1489:                case 9:
1490:                    return new Color(51, 153, 204, opacity); // Mid blue-green
1491:                case 10:
1492:                    return new Color(102, 255, 153, opacity); // lime
1493:                case 11:
1494:                    return new Color(51, 0, 204, opacity); // purple
1495:                case 12:
1496:                    return new Color(0, 204, 102, opacity); // emerald
1497:                case 13:
1498:                    return new Color(0, 153, 255, opacity); // bright blue-green
1499:                case 14:
1500:                    return new Color(255, 204, 0, opacity); // Orange
1501:                case 15:
1502:                    return new Color(0, 204, 255, opacity); // bright blue
1503:                case 16:
1504:                    return new Color(153, 00, 51, opacity); // ruby
1505:                case 17:
1506:                    return new Color(153, 51, 0, opacity); // brown
1507:                }
1508:                return Color.gray;
1509:            }
1510:
1511:            /**
1512:             * Darken the selected color for the sides of the pie chart
1513:             * @param c the selected color
1514:             * @param degrees the angle/position at which the color is used
1515:             * @return the new color
1516:             */
1517:            protected Color darken(Color c, double degrees) {
1518:                degrees = Math.min(360.0, Math.max(degrees, 0.0));
1519:                double multiplier = 1.0 - (degrees % 180.0) / 360.0;
1520:                return new Color((int) (c.getRed() * multiplier), (int) (c
1521:                        .getGreen() * multiplier),
1522:                        (int) (c.getBlue() * multiplier), c.getAlpha());
1523:            }
1524:
1525:            /**
1526:             * Generate an HSB color, using the index as an offset int the H color range
1527:             * @param idx the index of the color
1528:             * @return the new color
1529:             */
1530:            protected Color getGeneratedColor(int idx) {
1531:                int opacity = (int) (255 * (1.0 - Math.max(animationScale, 0.0)));
1532:                opacity = Math.max(0, Math.min(255, opacity));
1533:                return getGeneratedColor(idx, opacity);
1534:            }
1535:
1536:            /**
1537:             * Get a generated color with partial opacity
1538:             * @param i the color index
1539:             * @param opacity the opacity in the range 0-255
1540:             * @return the new color
1541:             */
1542:            protected Color getGeneratedColor(int i, int opacity) {
1543:                opacity = Math.min(255, Math.max(opacity, 0));
1544:                Color c = Color.getHSBColor((0.9F * i / numSeries), 0.9F, 0.9F);
1545:                return new Color(c.getRed(), c.getGreen(), c.getBlue(), opacity);
1546:            }
1547:
1548:            /**
1549:             * Set the display mode of the chart
1550:             * @param newMode the new mode: 0=BAR, 1=STACKEDBAR, 2=LINE, 3=PIE, 4=SCATTER
1551:             */
1552:            public void setMode(int newMode) {
1553:                mode = newMode;
1554:                backgroundImage = null;
1555:                highlightedArea = null;
1556:
1557:                if (newPoints != null)
1558:                    showNewData();
1559:            }
1560:
1561:            /**
1562:             * Get the current display mode of the chart
1563:             * @return the mode: 0=BAR, 1=STACKEDBAR, 2=LINE, 3=PIE, 4=SCATTER
1564:             */
1565:            public int getMode() {
1566:                return mode;
1567:            }
1568:
1569:            /**
1570:             * Set the text of the form to the specified language string,
1571:             * the setLangString(...) function is the preferred method of setting
1572:             * the caption.
1573:             * @param newStr the new title
1574:             */
1575:            public void setTitle(String newStr) {
1576:                title = newStr;
1577:                repaint();
1578:            }
1579:
1580:            /**
1581:             * Get the graph title
1582:             * @return the title text
1583:             */
1584:            public String getTitle() {
1585:                return title;
1586:            }
1587:
1588:            /**
1589:             * Set one or more attributes of the component. Currently this handles the
1590:             * attributes
1591:             * <OL>
1592:             * <LI>mode, value=the type of chart i.e. 0=BAR, 1=STACKEDBAR, 2=LINE, 3=PIE, 4=SCATTER</LI>
1593:             * <LI>border, value=1 or true to surround with a border</LI>
1594:             * <LI>labels, value=1 or true to label the data points</LI>
1595:             * <LI>markers, value=1 or true to draw markers for each data point</LI>
1596:             * <LI>minY, value=the minimum allowable y value, any value less than this will not be drawn </LI>
1597:             * <LI>legendPos, value=the legend position, 0=top left, 1=top right, 2=bottom left, 3=bottom right, 4=right margin, 5=bottom margin</LI>
1598:             * <LI>highlight, value=1 or true to highlight the selection under the mouse</LI>
1599:             * </OL>
1600:             * @param attribName the attribute name
1601:             * @param attribValue the attribute value
1602:             * @return 0 for success, non zero otherwise
1603:             */
1604:            public int setAttribute(String attribName, Object attribValue) {
1605:                String attribNameLwr = attribName.toLowerCase();
1606:                String attribValueStr = (String) attribValue;
1607:                String attribValueLwr = attribValueStr.toLowerCase();
1608:                if (attribNameLwr.equals("mode"))
1609:                    mode = new Integer(attribValueStr).intValue();
1610:                else if (attribNameLwr.equals("border"))
1611:                    drawBorder = (attribValueLwr.equals("true") || attribValueLwr
1612:                            .equals("1"));
1613:                else if (attribNameLwr.equals("labels"))
1614:                    labelPoints = (attribValueLwr.equals("true") || attribValueLwr
1615:                            .equals("1"));
1616:                else if (attribNameLwr.equals("markers"))
1617:                    drawPoints = (attribValueLwr.equals("true") || attribValueLwr
1618:                            .equals("1"));
1619:                else if (attribNameLwr.equals("minY"))
1620:                    minYAllowable = Double.parseDouble(attribValueStr);
1621:                else if (attribNameLwr.equals("legendpos"))
1622:                    legendPos = Integer.parseInt(attribValueStr);
1623:                else if (attribNameLwr.equals("highlight"))
1624:                    highlightSelection = (attribValueLwr.equals("true") || attribValueLwr
1625:                            .equals("1"));
1626:
1627:                repaint(100);
1628:
1629:                return 0;
1630:            }
1631:
1632:            //----------------------------------------------------------------------------
1633:
1634:            private class ChartPanel extends JComponent implements 
1635:                    MouseMotionListener {
1636:                public ChartPanel() {
1637:                    addMouseMotionListener(this );
1638:                }
1639:
1640:                /**
1641:                 * Renders the chart.
1642:                 */
1643:                public void paintComponent(Graphics g) {
1644:                    Graphics2D g2d = (Graphics2D) g;
1645:
1646:                    g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
1647:                            RenderingHints.VALUE_RENDER_QUALITY);
1648:
1649:                    // draw the title
1650:                    if (fm == null)
1651:                        fm = getFontMetrics(getFont());
1652:                    if (labelHeight == 0) {
1653:                        labelHeight = fm.getHeight();
1654:                        labelWidth = Math.max(fm.stringWidth(new Integer(
1655:                                (int) maxX).toString()),
1656:                                fm.stringWidth(new Integer((int) maxY)
1657:                                        .toString())) + 2;
1658:                        titleHeight = labelHeight * 2;
1659:                        top = padding + titleHeight;
1660:                        bottom = getSize().height - padding - labelHeight;
1661:                        if (xTitle != null)
1662:                            bottom -= padding + labelHeight;
1663:                        left = padding + labelWidth;
1664:                        if (yTitle != null)
1665:                            left += padding + labelWidth;
1666:                        right = getSize().width - padding
1667:                                - getLegendSpace(false);
1668:                    }
1669:
1670:                    if ((points != null) && (points.length > 0)) {
1671:                        scale = 1.0 - Math.max(Math.min(animationScale, 1.0),
1672:                                0.0);
1673:                        scale *= scale;
1674:                        if (!animateYAxis)
1675:                            scale = 1.0;
1676:
1677:                        if ((scale < 1.0) || (animationScale > 0.0))
1678:                            selectableAreas.clear();
1679:
1680:                        switch (mode) {
1681:                        case PIECHART:
1682:                            drawPieChart(g2d);
1683:                            break;
1684:
1685:                        case STACKEDBARCHART:
1686:                            drawStackedBarChart(g2d);
1687:                            break;
1688:
1689:                        case BARCHART:
1690:                            drawBarChart(g2d);
1691:                            break;
1692:
1693:                        case SCATTERCHART:
1694:                        case LINECHART:
1695:                        default:
1696:                            drawLineChart(g2d);
1697:                            break;
1698:                        }
1699:                        drawSelectedArea(g2d);
1700:                    }
1701:
1702:                    if ((animationScale <= 0.0) || (animationScale > 1.0)) {
1703:                        animationTimer.stop();
1704:                        if (animationScaleInc < 0.0)
1705:                            showNewData();
1706:                    }
1707:                }
1708:
1709:                /**
1710:                 * Invoked when a mouse button is pressed on a component and then 
1711:                 * dragged.  <code>MOUSE_DRAGGED</code> events will continue to be 
1712:                 * delivered to the component where the drag originated until the 
1713:                 * mouse button is released (regardless of whether the mouse position 
1714:                 * is within the bounds of the component).
1715:                 * <p> 
1716:                 * Due to platform-dependent Drag&Drop implementations, 
1717:                 * <code>MOUSE_DRAGGED</code> events may not be delivered during a native 
1718:                 * Drag&Drop operation.  
1719:                 */
1720:                public void mouseDragged(MouseEvent e) {
1721:                }
1722:
1723:                /**
1724:                 * Invoked when the mouse cursor has been moved onto a component
1725:                 * but no buttons have been pushed.
1726:                 */
1727:                public void mouseMoved(MouseEvent e) {
1728:                    int numAreas = selectableAreas.size();
1729:                    for (int i = 0; i < numAreas; i++) {
1730:                        SelectableArea area = (SelectableArea) selectableAreas
1731:                                .get(i);
1732:                        Point pt = e.getPoint();
1733:                        if (area.area.contains(pt.x, pt.y)) {
1734:                            highlightedArea = area;
1735:                            setToolTipText(area.series + ", " + area.label
1736:                                    + ": " + area.value);
1737:                            repaint();
1738:                            return;
1739:                        }
1740:                    }
1741:                    highlightedArea = null;
1742:                    setToolTipText("");
1743:                    repaint();
1744:                }
1745:            }
1746:
1747:            /**
1748:             * An area of the chart that can be selected, with information about that area
1749:             */
1750:            public class SelectableArea {
1751:                /**
1752:                 * the value of this area
1753:                 */
1754:                public double value;
1755:
1756:                /**
1757:                 * the label (x-value) of this area
1758:                 */
1759:                public String label;
1760:
1761:                /**
1762:                 * the series name
1763:                 */
1764:                public String series;
1765:
1766:                /**
1767:                 * The selected area
1768:                 */
1769:                public Area area;
1770:
1771:                /**
1772:                 * The displayed color
1773:                 */
1774:                public Color color;
1775:
1776:                /**
1777:                 * Create a new selectable area
1778:                 * @param l the label
1779:                 * @param s the series name
1780:                 * @param v the value
1781:                 * @param a the area
1782:                 * @param clr the color
1783:                 */
1784:                public SelectableArea(String l, String s, double v, Area a,
1785:                        Color clr) {
1786:                    label = l;
1787:                    series = s;
1788:                    value = v;
1789:                    area = a;
1790:                    color = clr;
1791:                }
1792:            }
1793:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.