00001
00002
00003
00004
00005
00006
00007 package org.swtchart.internal.series;
00008
00009 import java.util.ArrayList;
00010 import java.util.List;
00011
00012 import org.eclipse.swt.SWT;
00013 import org.eclipse.swt.graphics.Color;
00014 import org.eclipse.swt.graphics.GC;
00015 import org.eclipse.swt.widgets.Display;
00016 import org.swtchart.Chart;
00017 import org.swtchart.IAxis.Direction;
00018 import org.swtchart.ILineSeries;
00019 import org.swtchart.LineStyle;
00020 import org.swtchart.Range;
00021 import org.swtchart.internal.Util;
00022 import org.swtchart.internal.axis.Axis;
00023 import org.swtchart.internal.compress.CompressLineSeries;
00024 import org.swtchart.internal.compress.CompressScatterSeries;
00025
00029 public class LineSeries extends Series implements ILineSeries {
00030
00032 private int symbolSize;
00033
00035 private Color symbolColor;
00036
00038 private Color[] symbolColors;
00039
00041 private PlotSymbolType symbolType;
00042
00044 private LineStyle lineStyle;
00045
00047 private Color lineColor;
00048
00050 private int lineWidth;
00051
00053 private boolean areaEnabled;
00054
00056 private boolean stepEnabled;
00057
00059 private int antialias;
00060
00062 private static final int ALPHA = 50;
00063
00065 private static final LineStyle DEFAULT_LINE_STYLE = LineStyle.SOLID;
00066
00068 private static final int DEFAULT_LINE_WIDTH = 1;
00069
00071 private static final int DEFAULT_LINE_COLOR = SWT.COLOR_BLUE;
00072
00074 private static final int DEFAULT_SYMBOL_COLOR = SWT.COLOR_DARK_GRAY;
00075
00077 private static final int DEFAULT_SIZE = 4;
00078
00080 private static final PlotSymbolType DEFAULT_SYMBOL_TYPE = PlotSymbolType.CIRCLE;
00081
00083 private static final int DEFAULT_ANTIALIAS = SWT.DEFAULT;
00084
00086 private static final int MARGIN_AT_MIN_MAX_PLOT = 6;
00087
00096 protected LineSeries(Chart chart, String id) {
00097 super(chart, id);
00098
00099 symbolSize = 4;
00100 symbolColor = Display.getDefault().getSystemColor(DEFAULT_SYMBOL_COLOR);
00101 symbolType = DEFAULT_SYMBOL_TYPE;
00102
00103 lineStyle = DEFAULT_LINE_STYLE;
00104 lineColor = Display.getDefault().getSystemColor(DEFAULT_LINE_COLOR);
00105
00106 areaEnabled = false;
00107
00108 antialias = DEFAULT_ANTIALIAS;
00109 lineWidth = DEFAULT_LINE_WIDTH;
00110
00111 compressor = new CompressLineSeries();
00112 symbolColors = new Color[0];
00113 }
00114
00115
00116
00117
00118 public LineStyle getLineStyle() {
00119 return lineStyle;
00120 }
00121
00122
00123
00124
00125 public void setLineStyle(LineStyle style) {
00126 if (style == null) {
00127 this.lineStyle = DEFAULT_LINE_STYLE;
00128 return;
00129 }
00130
00131 this.lineStyle = style;
00132 if (compressor instanceof CompressScatterSeries) {
00133 ((CompressScatterSeries) compressor)
00134 .setLineVisible(style != LineStyle.NONE);
00135 }
00136 }
00137
00138
00139
00140
00141 public Color getLineColor() {
00142 if (lineColor.isDisposed()) {
00143 lineColor = Display.getDefault().getSystemColor(DEFAULT_LINE_COLOR);
00144 }
00145 return lineColor;
00146 }
00147
00148
00149
00150
00151 public void setLineColor(Color color) {
00152 if (color != null && color.isDisposed()) {
00153 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
00154 }
00155
00156 if (color == null) {
00157 this.lineColor = Display.getDefault().getSystemColor(
00158 DEFAULT_LINE_COLOR);
00159 } else {
00160 this.lineColor = color;
00161 }
00162 }
00163
00164
00165
00166
00167 public int getLineWidth() {
00168 return lineWidth;
00169 }
00170
00171
00172
00173
00174 public void setLineWidth(int width) {
00175 if (width <= 0) {
00176 this.lineWidth = DEFAULT_LINE_WIDTH;
00177 } else {
00178 this.lineWidth = width;
00179 }
00180 }
00181
00182
00183
00184
00185 public PlotSymbolType getSymbolType() {
00186 return symbolType;
00187 }
00188
00189
00190
00191
00192 public void setSymbolType(PlotSymbolType type) {
00193 if (type == null) {
00194 this.symbolType = DEFAULT_SYMBOL_TYPE;
00195 } else {
00196 this.symbolType = type;
00197 }
00198 }
00199
00200
00201
00202
00203 public int getSymbolSize() {
00204 return symbolSize;
00205 }
00206
00207
00208
00209
00210 public void setSymbolSize(int size) {
00211 if (size <= 0) {
00212 this.symbolSize = DEFAULT_SIZE;
00213 } else {
00214 this.symbolSize = size;
00215 }
00216 }
00217
00218
00219
00220
00221 public Color getSymbolColor() {
00222 return symbolColor;
00223 }
00224
00225
00226
00227
00228 public void setSymbolColor(Color color) {
00229 if (color != null && color.isDisposed()) {
00230 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
00231 }
00232
00233 if (color == null) {
00234 this.symbolColor = Display.getDefault().getSystemColor(
00235 DEFAULT_SYMBOL_COLOR);
00236 } else {
00237 this.symbolColor = color;
00238 }
00239 }
00240
00241
00242
00243
00244 public Color[] getSymbolColors() {
00245 Color[] copiedSymbolColors = new Color[symbolColors.length];
00246 System.arraycopy(symbolColors, 0, copiedSymbolColors, 0,
00247 symbolColors.length);
00248
00249 return copiedSymbolColors;
00250 }
00251
00252
00253
00254
00255 public void setSymbolColors(Color[] colors) {
00256 if (colors == null) {
00257 symbolColors = new Color[0];
00258 return;
00259 }
00260
00261 for (Color color : colors) {
00262 if (color.isDisposed()) {
00263 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
00264 }
00265 }
00266
00267 symbolColors = new Color[colors.length];
00268 System.arraycopy(colors, 0, symbolColors, 0, colors.length);
00269 }
00270
00271
00272
00273
00274 @Override
00275 protected void setCompressor() {
00276 if (isXMonotoneIncreasing) {
00277 compressor = new CompressLineSeries();
00278 } else {
00279 compressor = new CompressScatterSeries();
00280 ((CompressScatterSeries) compressor)
00281 .setLineVisible(getLineStyle() != LineStyle.NONE);
00282 }
00283 }
00284
00285
00286
00287
00288 public void enableArea(boolean enabled) {
00289 areaEnabled = enabled;
00290 }
00291
00292
00293
00294
00295 public boolean isAreaEnabled() {
00296 return areaEnabled;
00297 }
00298
00299
00300
00301
00302 public void enableStep(boolean enabled) {
00303 stepEnabled = enabled;
00304 }
00305
00306
00307
00308
00309 public boolean isStepEnabled() {
00310 return stepEnabled;
00311 }
00312
00313
00314
00315
00316 @Override
00317 public Range getAdjustedRange(Axis axis, int length) {
00318
00319 Range range;
00320 if (axis.getDirection() == Direction.X) {
00321 range = getXRange();
00322 } else {
00323 range = getYRange();
00324 }
00325
00326 int lowerPlotMargin = getSymbolSize() + MARGIN_AT_MIN_MAX_PLOT;
00327 int upperPlotMargin = getSymbolSize() + MARGIN_AT_MIN_MAX_PLOT;
00328
00329 return getRangeWithMargin(lowerPlotMargin, upperPlotMargin, length,
00330 axis, range);
00331 }
00332
00333
00334
00335
00336 public int getAntialias() {
00337 return antialias;
00338 }
00339
00340
00341
00342
00343 public void setAntialias(int antialias) {
00344 if (antialias != SWT.DEFAULT && antialias != SWT.ON
00345 && antialias != SWT.OFF) {
00346 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
00347 }
00348 this.antialias = antialias;
00349 }
00350
00368 private int[] getLinePoints(double[] xseries, double[] yseries,
00369 int[] indexes, int index, Axis xAxis, Axis yAxis) {
00370
00371 int x1 = xAxis.getPixelCoordinate(xseries[index]);
00372 int x2 = xAxis.getPixelCoordinate(xseries[index + 1]);
00373 int x3 = x2;
00374 int x4 = x1;
00375 int y1 = yAxis.getPixelCoordinate(yseries[index]);
00376 int y2 = yAxis.getPixelCoordinate(yseries[index + 1]);
00377 int y3, y4;
00378
00379 double baseYCoordinate = yAxis.getRange().lower > 0 ? yAxis.getRange().lower
00380 : 0;
00381
00382 if (yAxis.isLogScaleEnabled()) {
00383 y3 = yAxis.getPixelCoordinate(yAxis.getRange().lower);
00384 y4 = y3;
00385 } else if (isValidStackSeries()) {
00386 y1 = yAxis.getPixelCoordinate(stackSeries[indexes[index]]);
00387 y2 = yAxis.getPixelCoordinate(stackSeries[indexes[index + 1]]);
00388 y3 = yAxis.getPixelCoordinate(stackSeries[indexes[index + 1]])
00389 + Math.abs(yAxis.getPixelCoordinate(yseries[index + 1])
00390 - yAxis.getPixelCoordinate(0))
00391 * (xAxis.isHorizontalAxis() ? 1 : -1);
00392 y4 = yAxis.getPixelCoordinate(stackSeries[indexes[index]])
00393 + Math.abs(yAxis.getPixelCoordinate(yseries[index])
00394 - yAxis.getPixelCoordinate(0))
00395 * (xAxis.isHorizontalAxis() ? 1 : -1);
00396 } else {
00397 y3 = yAxis.getPixelCoordinate(baseYCoordinate);
00398 y4 = y3;
00399 }
00400
00401 if (xAxis.isHorizontalAxis()) {
00402 return new int[] { x1, y1, x2, y2, x3, y3, x4, y4 };
00403 }
00404 return new int[] { y1, x1, y2, x2, y3, x3, y4, x4 };
00405 }
00406
00407
00408
00409
00410 @Override
00411 protected void draw(GC gc, int width, int height, Axis xAxis, Axis yAxis) {
00412 int oldAntialias = gc.getAntialias();
00413 int oldLineWidth = gc.getLineWidth();
00414 gc.setAntialias(antialias);
00415 gc.setLineWidth(lineWidth);
00416
00417 if (lineStyle != LineStyle.NONE) {
00418 drawLineAndArea(gc, width, height, xAxis, yAxis);
00419 }
00420
00421 if (symbolType != PlotSymbolType.NONE || getLabel().isVisible()
00422 || getXErrorBar().isVisible() || getYErrorBar().isVisible()) {
00423 drawSymbolAndLabel(gc, width, height, xAxis, yAxis);
00424 }
00425
00426 gc.setAntialias(oldAntialias);
00427 gc.setLineWidth(oldLineWidth);
00428 }
00429
00444 private void drawLineAndArea(GC gc, int width, int height, Axis xAxis,
00445 Axis yAxis) {
00446
00447
00448 double[] xseries = compressor.getCompressedXSeries();
00449 double[] yseries = compressor.getCompressedYSeries();
00450 if (xseries.length == 0 || yseries.length == 0) {
00451 return;
00452 }
00453 int[] indexes = compressor.getCompressedIndexes();
00454 if (xAxis.isValidCategoryAxis()) {
00455 for (int i = 0; i < xseries.length; i++) {
00456 xseries[i] = indexes[i];
00457 }
00458 }
00459
00460 gc.setLineStyle(Util.getIndexDefinedInSWT(lineStyle));
00461 Color oldForeground = gc.getForeground();
00462 gc.setForeground(getLineColor());
00463
00464 boolean isHorizontal = xAxis.isHorizontalAxis();
00465 if (stepEnabled || areaEnabled || stackEnabled) {
00466 for (int i = 0; i < xseries.length - 1; i++) {
00467 int[] p = getLinePoints(xseries, yseries, indexes, i, xAxis,
00468 yAxis);
00469
00470
00471 if (lineStyle != LineStyle.NONE) {
00472 if (stepEnabled) {
00473 if (isHorizontal) {
00474 gc.drawLine(p[0], p[1], p[2], p[1]);
00475 gc.drawLine(p[2], p[1], p[2], p[3]);
00476 } else {
00477 gc.drawLine(p[0], p[1], p[0], p[3]);
00478 gc.drawLine(p[0], p[3], p[2], p[3]);
00479 }
00480 } else {
00481 gc.drawLine(p[0], p[1], p[2], p[3]);
00482 }
00483 }
00484
00485
00486 if (areaEnabled) {
00487 drawArea(gc, p, isHorizontal);
00488 }
00489 }
00490 } else {
00491 if (lineStyle == LineStyle.SOLID) {
00492 drawLine(gc, xAxis, yAxis, xseries, yseries, isHorizontal);
00493 } else if (lineStyle != LineStyle.NONE) {
00494 drawLineWithStyle(gc, xAxis, yAxis, xseries, yseries,
00495 isHorizontal);
00496 }
00497 }
00498
00499 gc.setForeground(oldForeground);
00500 }
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510 private static void drawLine(GC gc, Axis xAxis, Axis yAxis,
00511 double[] xseries, double[] yseries, boolean isHorizontal) {
00512 double xLower = xAxis.getRange().lower;
00513 double xUpper = xAxis.getRange().upper;
00514 double yLower = yAxis.getRange().lower;
00515 double yUpper = yAxis.getRange().upper;
00516
00517 int prevX = xAxis.getPixelCoordinate(xseries[0], xLower, xUpper);
00518 int prevY = yAxis.getPixelCoordinate(yseries[0], yLower, yUpper);
00519
00520 boolean drawVerticalLine = false;
00521 int verticalLineYLower = 0;
00522 int verticalLineYUpper = 0;
00523
00524 for (int i = 0; i < xseries.length - 1; i++) {
00525 if (isInvalidSeries(yseries[i + 1])) {
00526 continue;
00527 }
00528 int x = xAxis.getPixelCoordinate(xseries[i + 1], xLower, xUpper);
00529 int y = yAxis.getPixelCoordinate(yseries[i + 1], yLower, yUpper);
00530
00531 if (x == prevX && i < xseries.length - 2) {
00532 if (drawVerticalLine) {
00533
00534 verticalLineYLower = Math.min(verticalLineYLower, y);
00535 verticalLineYUpper = Math.max(verticalLineYUpper, y);
00536 } else {
00537
00538 verticalLineYLower = Math.min(prevY, y);
00539 verticalLineYUpper = Math.max(prevY, y);
00540 drawVerticalLine = true;
00541 }
00542 } else {
00543
00544
00545 if (drawVerticalLine) {
00546 if (isHorizontal) {
00547 gc.drawLine(prevX, verticalLineYLower, prevX,
00548 verticalLineYUpper);
00549 } else {
00550 gc.drawLine(verticalLineYLower, prevX,
00551 verticalLineYUpper, prevX);
00552 }
00553 drawVerticalLine = false;
00554 }
00555
00556
00557 if (isHorizontal) {
00558 gc.drawLine(prevX, prevY, x, y);
00559 } else {
00560 gc.drawLine(prevY, prevX, y, x);
00561 }
00562 }
00563
00564 prevX = x;
00565 prevY = y;
00566 }
00567 }
00568
00593 private static void drawLineWithStyle(GC gc, Axis xAxis, Axis yAxis,
00594 double[] xseries, double[] yseries, boolean isHorizontal) {
00595 double xLower = xAxis.getRange().lower;
00596 double xUpper = xAxis.getRange().upper;
00597 double yLower = yAxis.getRange().lower;
00598 double yUpper = yAxis.getRange().upper;
00599
00600 List<Integer> pointList = new ArrayList<Integer>();
00601 int prevX = xAxis.getPixelCoordinate(xseries[0], xLower, xUpper);
00602 int prevY = yAxis.getPixelCoordinate(yseries[0], yLower, yUpper);
00603
00604
00605 addPoint(pointList, prevX, prevY, isHorizontal);
00606
00607 boolean drawVerticalLine = false;
00608 int verticalLineYLower = 0;
00609 int verticalLineYUpper = 0;
00610
00611 for (int i = 0; i < xseries.length - 1; i++) {
00612 int x = xAxis.getPixelCoordinate(xseries[i + 1], xLower, xUpper);
00613 int y = yAxis.getPixelCoordinate(yseries[i + 1], yLower, yUpper);
00614
00615 if (x == prevX && i < xseries.length - 2) {
00616 if (drawVerticalLine) {
00617
00618 verticalLineYLower = Math.min(verticalLineYLower, y);
00619 verticalLineYUpper = Math.max(verticalLineYUpper, y);
00620 } else {
00621
00622 verticalLineYLower = Math.min(prevY, y);
00623 verticalLineYUpper = Math.max(prevY, y);
00624 drawVerticalLine = true;
00625 }
00626 } else {
00627
00628
00629 if (drawVerticalLine) {
00630 addPoint(pointList, prevX, verticalLineYLower, isHorizontal);
00631 addPoint(pointList, prevX, verticalLineYUpper, isHorizontal);
00632 addPoint(pointList, prevX, prevY, isHorizontal);
00633 }
00634
00635
00636 addPoint(pointList, x, y, isHorizontal);
00637
00638 drawVerticalLine = false;
00639 }
00640
00641 prevX = x;
00642 prevY = y;
00643 }
00644
00645 int[] polyline = new int[pointList.size()];
00646 for (int i = 0; i < polyline.length; i++) {
00647 polyline[i] = pointList.get(i);
00648 }
00649
00650 boolean advanced = gc.getAdvanced();
00651 gc.setAdvanced(true);
00652 gc.drawPolyline(polyline);
00653 gc.setAdvanced(advanced);
00654 }
00655
00656 private static void addPoint(List<Integer> pointList, int x, int y,
00657 boolean isHorizontal) {
00658 if (isHorizontal) {
00659 pointList.add(Integer.valueOf(x));
00660 pointList.add(Integer.valueOf(y));
00661 } else {
00662 pointList.add(Integer.valueOf(y));
00663 pointList.add(Integer.valueOf(x));
00664 }
00665 }
00666
00677 private void drawArea(GC gc, int[] p, boolean isHorizontal) {
00678 int alpha = gc.getAlpha();
00679 gc.setAlpha(ALPHA);
00680 Color oldBackground = gc.getBackground();
00681 gc.setBackground(getLineColor());
00682
00683 int[] pointArray;
00684 if (stepEnabled) {
00685 if (isHorizontal) {
00686 pointArray = new int[] { p[0], p[1], p[2], p[1], p[4], p[7],
00687 p[6], p[7], p[0], p[1] };
00688 } else {
00689 pointArray = new int[] { p[0], p[1], p[0], p[3], p[6], p[5],
00690 p[6], p[7], p[0], p[1] };
00691 }
00692 } else {
00693 pointArray = new int[] { p[0], p[1], p[2], p[3], p[4], p[5], p[6],
00694 p[7], p[0], p[1] };
00695 }
00696
00697 gc.fillPolygon(pointArray);
00698
00699 gc.setAlpha(alpha);
00700 gc.setBackground(oldBackground);
00701 }
00702
00717 private void drawSymbolAndLabel(GC gc, int width, int height, Axis xAxis,
00718 Axis yAxis) {
00719
00720
00721 double[] xseries = compressor.getCompressedXSeries();
00722 double[] yseries = compressor.getCompressedYSeries();
00723 int[] indexes = compressor.getCompressedIndexes();
00724 if (xAxis.isValidCategoryAxis()) {
00725 boolean isValidStackSeries = isValidStackSeries();
00726 for (int i = 0; i < xseries.length; i++) {
00727 xseries[i] = indexes[i];
00728 if (isValidStackSeries) {
00729 yseries[i] = stackSeries[indexes[i]];
00730 }
00731 }
00732 }
00733
00734
00735 for (int i = 0; i < xseries.length; i++) {
00736 if (Series.isInvalidSeries(yseries[i]))
00737 continue;
00738
00739 Color color;
00740 if (symbolColors.length > indexes[i]) {
00741 color = symbolColors[indexes[i]];
00742 } else {
00743 color = getSymbolColor();
00744 }
00745 int h, v;
00746 if (xAxis.isHorizontalAxis()) {
00747 h = xAxis.getPixelCoordinate(xseries[i]);
00748 v = yAxis.getPixelCoordinate(yseries[i]);
00749 } else {
00750 v = xAxis.getPixelCoordinate(xseries[i]);
00751 h = yAxis.getPixelCoordinate(yseries[i]);
00752 }
00753 if (getSymbolType() != PlotSymbolType.NONE) {
00754 drawSeriesSymbol(gc, h, v, color);
00755 }
00756 seriesLabel.draw(gc, h, v, yseries[i], indexes[i], SWT.BOTTOM);
00757 xErrorBar.draw(gc, h, v, xAxis, indexes[i]);
00758 yErrorBar.draw(gc, h, v, yAxis, indexes[i]);
00759 }
00760 }
00761
00774 public void drawSeriesSymbol(GC gc, int h, int v, Color color) {
00775 int oldAntialias = gc.getAntialias();
00776 gc.setAntialias(SWT.ON);
00777 Color oldForeground = gc.getForeground();
00778 gc.setForeground(color);
00779 Color oldBackground = gc.getBackground();
00780 gc.setBackground(color);
00781
00782 switch (symbolType) {
00783 case CIRCLE:
00784 gc.fillOval(h - symbolSize, v - symbolSize, symbolSize * 2,
00785 symbolSize * 2);
00786 break;
00787 case SQUARE:
00788 gc.fillRectangle(h - symbolSize, v - symbolSize, symbolSize * 2,
00789 symbolSize * 2);
00790 break;
00791 case DIAMOND:
00792 int[] diamondArray = { h, v - symbolSize, h + symbolSize, v, h,
00793 v + symbolSize, h - symbolSize, v };
00794 gc.fillPolygon(diamondArray);
00795 break;
00796 case TRIANGLE:
00797 int[] triangleArray = { h, v - symbolSize, h + symbolSize,
00798 v + symbolSize, h - symbolSize, v + symbolSize };
00799 gc.fillPolygon(triangleArray);
00800 break;
00801 case INVERTED_TRIANGLE:
00802 int[] invertedTriangleArray = { h, v + symbolSize, h + symbolSize,
00803 v - symbolSize, h - symbolSize, v - symbolSize };
00804 gc.fillPolygon(invertedTriangleArray);
00805 break;
00806 case CROSS:
00807 gc.setLineStyle(SWT.LINE_SOLID);
00808 gc.drawLine(h - symbolSize, v - symbolSize, h + symbolSize, v
00809 + symbolSize);
00810 gc.drawLine(h - symbolSize, v + symbolSize, h + symbolSize, v
00811 - symbolSize);
00812 break;
00813 case PLUS:
00814 gc.setLineStyle(SWT.LINE_SOLID);
00815 gc.drawLine(h, v - symbolSize, h, v + symbolSize);
00816 gc.drawLine(h - symbolSize, v, h + symbolSize, v);
00817 break;
00818 case NONE:
00819 default:
00820 break;
00821 }
00822 gc.setAntialias(oldAntialias);
00823 gc.setBackground(oldBackground);
00824 gc.setForeground(oldForeground);
00825 }
00826 }