00001
00002
00003
00004
00005
00006
00007 package org.swtchart.internal.series;
00008
00009 import java.util.ArrayList;
00010 import java.util.Date;
00011 import java.util.List;
00012
00013 import org.eclipse.swt.SWT;
00014 import org.eclipse.swt.graphics.GC;
00015 import org.eclipse.swt.graphics.Point;
00016 import org.eclipse.swt.widgets.Event;
00017 import org.swtchart.Chart;
00018 import org.swtchart.IAxis;
00019 import org.swtchart.IDisposeListener;
00020 import org.swtchart.IErrorBar;
00021 import org.swtchart.ISeries;
00022 import org.swtchart.ISeriesLabel;
00023 import org.swtchart.Range;
00024 import org.swtchart.IAxis.Direction;
00025 import org.swtchart.internal.axis.Axis;
00026 import org.swtchart.internal.compress.ICompress;
00027
00031 abstract public class Series implements ISeries {
00032
00034 protected static final SeriesType DEFAULT_SERIES_TYPE = SeriesType.LINE;
00035
00037 protected double[] xSeries;
00038
00040 protected double[] ySeries;
00041
00043 protected double minX;
00044
00046 protected double maxX;
00047
00049 protected double minY;
00050
00052 protected double maxY;
00053
00055 protected String id;
00056
00058 protected ICompress compressor;
00059
00061 protected int xAxisId;
00062
00064 protected int yAxisId;
00065
00067 protected boolean visible;
00068
00070 protected boolean isXMonotoneIncreasing;
00071
00073 protected SeriesType type;
00074
00076 protected SeriesLabel seriesLabel;
00077
00079 protected ErrorBar xErrorBar;
00080
00082 protected ErrorBar yErrorBar;
00083
00085 protected Chart chart;
00086
00088 protected boolean stackEnabled;
00089
00091 protected double[] stackSeries;
00092
00094 private boolean isDateSeries;
00095
00097 private boolean visibleInLegend;
00098
00100 private String description;
00101
00103 private List<IDisposeListener> listeners;
00104
00113 protected Series(Chart chart, String id) {
00114 super();
00115
00116 this.chart = chart;
00117 this.id = id;
00118 xAxisId = 0;
00119 yAxisId = 0;
00120 visible = true;
00121 type = DEFAULT_SERIES_TYPE;
00122 stackEnabled = false;
00123 isXMonotoneIncreasing = true;
00124 seriesLabel = new SeriesLabel();
00125 xErrorBar = new ErrorBar();
00126 yErrorBar = new ErrorBar();
00127 visibleInLegend = true;
00128 listeners = new ArrayList<IDisposeListener>();
00129 xSeries = new double[0];
00130 ySeries = new double[0];
00131 }
00132
00133
00134
00135
00136 public String getId() {
00137 return id;
00138 }
00139
00140
00141
00142
00143 public void setVisible(boolean visible) {
00144 if (this.visible == visible) {
00145 return;
00146 }
00147
00148 this.visible = visible;
00149
00150 ((SeriesSet) chart.getSeriesSet()).updateStackAndRiserData();
00151 }
00152
00153
00154
00155
00156 public boolean isVisible() {
00157 return visible;
00158 }
00159
00160
00161
00162
00163 public SeriesType getType() {
00164 return type;
00165 }
00166
00167
00168
00169
00170 public boolean isStackEnabled() {
00171 return stackEnabled;
00172 }
00173
00174
00175
00176
00177 public void enableStack(boolean enabled) {
00178 if (enabled && minY < 0) {
00179 throw new IllegalStateException(
00180 "Stacked series cannot contain minus values.");
00181 }
00182
00183 if (stackEnabled == enabled) {
00184 return;
00185 }
00186
00187 stackEnabled = enabled;
00188
00189 ((SeriesSet) chart.getSeriesSet()).updateStackAndRiserData();
00190 }
00191
00192
00193
00194
00195 public void setXSeries(double[] series) {
00196
00197 if (series == null) {
00198 SWT.error(SWT.ERROR_NULL_ARGUMENT);
00199 return;
00200 }
00201
00202 xSeries = new double[series.length];
00203 System.arraycopy(series, 0, xSeries, 0, series.length);
00204 isDateSeries = false;
00205
00206 if (xSeries.length == 0) {
00207 return;
00208 }
00209
00210
00211 minX = xSeries[0];
00212 maxX = xSeries[0];
00213 for (int i = 1; i < xSeries.length; i++) {
00214 if (minX > xSeries[i]) {
00215 minX = xSeries[i];
00216 }
00217 if (maxX < xSeries[i]) {
00218 maxX = xSeries[i];
00219 }
00220
00221 if (xSeries[i - 1] > xSeries[i]) {
00222 isXMonotoneIncreasing = false;
00223 }
00224 }
00225
00226 setCompressor();
00227
00228 compressor.setXSeries(xSeries);
00229 compressor.setYSeries(ySeries);
00230
00231 if (minX <= 0) {
00232 IAxis axis = chart.getAxisSet().getXAxis(xAxisId);
00233 if (axis != null) {
00234 axis.enableLogScale(false);
00235 }
00236 }
00237 }
00238
00239
00240
00241
00242 public double[] getXSeries() {
00243 double[] copiedSeries = new double[xSeries.length];
00244 System.arraycopy(xSeries, 0, copiedSeries, 0, xSeries.length);
00245
00246 return copiedSeries;
00247 }
00248
00249
00250
00251
00252 public void setYSeries(double[] series) {
00253
00254 if (series == null) {
00255 SWT.error(SWT.ERROR_NULL_ARGUMENT);
00256 return;
00257 }
00258
00259 ySeries = new double[series.length];
00260 System.arraycopy(series, 0, ySeries, 0, series.length);
00261
00262 if (ySeries.length == 0) {
00263 return;
00264 }
00265
00266
00267 minY = Double.MAX_VALUE;
00268 maxY = Double.MIN_VALUE;
00269 for (int i = 0; i < ySeries.length; i++) {
00270 if (isInvalidSeries(ySeries[i]))
00271 continue;
00272 if (minY > ySeries[i]) {
00273 minY = ySeries[i];
00274 }
00275 if (maxY < ySeries[i]) {
00276 maxY = ySeries[i];
00277 }
00278 }
00279
00280 if (xSeries.length != series.length) {
00281 xSeries = new double[series.length];
00282 for (int i = 0; i < series.length; i++) {
00283 xSeries[i] = i;
00284 }
00285 minX = xSeries[0];
00286 maxX = xSeries[xSeries.length - 1];
00287 isXMonotoneIncreasing = true;
00288 }
00289
00290 setCompressor();
00291
00292 compressor.setXSeries(xSeries);
00293 compressor.setYSeries(ySeries);
00294
00295 if (minX <= 0) {
00296 IAxis axis = chart.getAxisSet().getXAxis(xAxisId);
00297 if (axis != null) {
00298 axis.enableLogScale(false);
00299 }
00300 }
00301 if (minY <= 0) {
00302 IAxis axis = chart.getAxisSet().getYAxis(yAxisId);
00303 if (axis != null) {
00304 axis.enableLogScale(false);
00305 }
00306 stackEnabled = false;
00307 }
00308 }
00309
00310
00311
00312
00313 public double[] getYSeries() {
00314 double[] copiedSeries = new double[ySeries.length];
00315 System.arraycopy(ySeries, 0, copiedSeries, 0, ySeries.length);
00316
00317 return copiedSeries;
00318 }
00319
00320
00321
00322
00323 public void setXDateSeries(Date[] series) {
00324 if (series == null) {
00325 SWT.error(SWT.ERROR_NULL_ARGUMENT);
00326 return;
00327 }
00328
00329 double[] xDateSeries = new double[series.length];
00330 for (int i = 0; i < series.length; i++) {
00331 xDateSeries[i] = series[i].getTime();
00332 }
00333 setXSeries(xDateSeries);
00334 isDateSeries = true;
00335 }
00336
00337
00338
00339
00340 public Date[] getXDateSeries() {
00341 if (!isDateSeries) {
00342 return new Date[0];
00343 }
00344
00345 Date[] series = new Date[xSeries.length];
00346 for (int i = 0; i < series.length; i++) {
00347 series[i] = new Date((long) xSeries[i]);
00348 }
00349 return series;
00350 }
00351
00357 public boolean isDateSeries() {
00358 return isDateSeries;
00359 }
00360
00366 public boolean isValidStackSeries() {
00367 return stackEnabled
00368 && stackSeries != null
00369 && stackSeries.length > 0
00370 && !chart.getAxisSet().getYAxis(yAxisId).isLogScaleEnabled()
00371 && ((Axis) chart.getAxisSet().getXAxis(xAxisId))
00372 .isValidCategoryAxis();
00373 }
00374
00380 public Range getXRange() {
00381 return new Range(minX, maxX);
00382 }
00383
00394 abstract public Range getAdjustedRange(Axis axis, int length);
00395
00401 public Range getYRange() {
00402 double min = minY;
00403 double max = maxY;
00404 Axis xAxis = (Axis) chart.getAxisSet().getXAxis(xAxisId);
00405 if (isValidStackSeries() && xAxis.isValidCategoryAxis()) {
00406 for (int i = 0; i < stackSeries.length; i++) {
00407 if (max < stackSeries[i]) {
00408 max = stackSeries[i];
00409 }
00410 }
00411 }
00412 return new Range(min, max);
00413 }
00414
00420 protected ICompress getCompressor() {
00421 return compressor;
00422 }
00423
00427 abstract protected void setCompressor();
00428
00429
00430
00431
00432 public int getXAxisId() {
00433 return xAxisId;
00434 }
00435
00436
00437
00438
00439 public void setXAxisId(int id) {
00440 if (xAxisId == id) {
00441 return;
00442 }
00443
00444 IAxis axis = chart.getAxisSet().getXAxis(xAxisId);
00445
00446 if (minX <= 0 && axis != null && axis.isLogScaleEnabled()) {
00447 chart.getAxisSet().getXAxis(xAxisId).enableLogScale(false);
00448 }
00449
00450 xAxisId = id;
00451
00452 ((SeriesSet) chart.getSeriesSet()).updateStackAndRiserData();
00453 }
00454
00455
00456
00457
00458 public int getYAxisId() {
00459 return yAxisId;
00460 }
00461
00462
00463
00464
00465 public void setYAxisId(int id) {
00466 yAxisId = id;
00467 }
00468
00469
00470
00471
00472 public ISeriesLabel getLabel() {
00473 return seriesLabel;
00474 }
00475
00476
00477
00478
00479 public IErrorBar getXErrorBar() {
00480 return xErrorBar;
00481 }
00482
00483
00484
00485
00486 public IErrorBar getYErrorBar() {
00487 return yErrorBar;
00488 }
00489
00496 protected void setStackSeries(double[] stackSeries) {
00497 this.stackSeries = stackSeries;
00498 }
00499
00500
00501
00502
00503 public Point getPixelCoordinates(int index) {
00504
00505
00506 IAxis hAxis;
00507 IAxis vAxis;
00508 if (chart.getOrientation() == SWT.HORIZONTAL) {
00509 hAxis = chart.getAxisSet().getXAxis(xAxisId);
00510 vAxis = chart.getAxisSet().getYAxis(yAxisId);
00511 } else if (chart.getOrientation() == SWT.VERTICAL) {
00512 hAxis = chart.getAxisSet().getYAxis(yAxisId);
00513 vAxis = chart.getAxisSet().getXAxis(xAxisId);
00514 } else {
00515 throw new IllegalStateException("unknown chart orientation");
00516 }
00517
00518
00519 return new Point(getPixelCoordinate(hAxis, index), getPixelCoordinate(
00520 vAxis, index));
00521 }
00522
00532 private int getPixelCoordinate(IAxis axis, int index) {
00533
00534
00535 double dataCoordinate;
00536 if (axis.getDirection() == Direction.X) {
00537 if (axis.isCategoryEnabled()) {
00538 dataCoordinate = index;
00539 } else {
00540 if (index < 0 || xSeries.length <= index) {
00541 throw new IllegalArgumentException(
00542 "Series index is out of range.");
00543 }
00544 dataCoordinate = xSeries[index];
00545 }
00546 } else if (axis.getDirection() == Direction.Y) {
00547 if (isValidStackSeries()) {
00548 if (index < 0 || stackSeries.length <= index) {
00549 throw new IllegalArgumentException(
00550 "Series index is out of range.");
00551 }
00552 dataCoordinate = stackSeries[index];
00553 } else {
00554 if (index < 0 || ySeries.length <= index) {
00555 throw new IllegalArgumentException(
00556 "Series index is out of range.");
00557 }
00558 dataCoordinate = ySeries[index];
00559 }
00560 } else {
00561 throw new IllegalStateException("unknown axis direction");
00562 }
00563
00564
00565 return axis.getPixelCoordinate(dataCoordinate);
00566 }
00567
00583 protected Range getRangeWithMargin(int lowerPlotMargin,
00584 int upperPlotMargin, int length, Axis axis, Range range) {
00585 if (length == 0) {
00586 return range;
00587 }
00588
00589 int lowerPixelCoordinate = axis.getPixelCoordinate(range.lower,
00590 range.lower, range.upper)
00591 + lowerPlotMargin
00592 * (axis.isHorizontalAxis() ? -1 : 1);
00593 int upperPixelCoordinate = axis.getPixelCoordinate(range.upper,
00594 range.lower, range.upper)
00595 + upperPlotMargin
00596 * (axis.isHorizontalAxis() ? 1 : -1);
00597
00598 double lower = axis.getDataCoordinate(lowerPixelCoordinate,
00599 range.lower, range.upper);
00600 double upper = axis.getDataCoordinate(upperPixelCoordinate,
00601 range.lower, range.upper);
00602
00603 return new Range(lower, upper);
00604 }
00605
00606
00607
00608
00609 public void setVisibleInLegend(boolean visible) {
00610 visibleInLegend = visible;
00611 }
00612
00613
00614
00615
00616 public boolean isVisibleInLegend() {
00617 return visibleInLegend;
00618 }
00619
00620
00621
00622
00623 public void setDescription(String description) {
00624 this.description = description;
00625 }
00626
00627
00628
00629
00630 public String getDescription() {
00631 return description;
00632 }
00633
00637 protected void dispose() {
00638 for (IDisposeListener listener : listeners) {
00639 listener.disposed(new Event());
00640 }
00641 }
00642
00643
00644
00645
00646 public void addDisposeListener(IDisposeListener listener) {
00647 listeners.add(listener);
00648 }
00649
00660 public void draw(GC gc, int width, int height) {
00661
00662 if (!visible || width < 0 || height < 0 || xSeries.length == 0
00663 || ySeries.length == 0) {
00664 return;
00665 }
00666
00667 Axis xAxis = (Axis) chart.getAxisSet().getXAxis(getXAxisId());
00668 Axis yAxis = (Axis) chart.getAxisSet().getYAxis(getYAxisId());
00669 if (xAxis == null || yAxis == null) {
00670 return;
00671 }
00672
00673 draw(gc, width, height, xAxis, yAxis);
00674 }
00675
00684 static protected boolean isInvalidSeries(double value) {
00685 return Double.isNaN(value);
00686 }
00687
00702 abstract protected void draw(GC gc, int width, int height, Axis xAxis,
00703 Axis yAxis);
00704 }