00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 package com.generalrobotix.ui.view.graph;
00011
00012 import org.eclipse.swt.SWT;
00013 import org.eclipse.swt.events.PaintEvent;
00014 import org.eclipse.swt.events.PaintListener;
00015 import org.eclipse.swt.graphics.Color;
00016 import org.eclipse.swt.graphics.FontMetrics;
00017 import org.eclipse.swt.graphics.GC;
00018 import org.eclipse.swt.graphics.RGB;
00019 import org.eclipse.swt.widgets.Canvas;
00020 import org.eclipse.swt.widgets.Composite;
00021
00022 import com.generalrobotix.ui.grxui.Activator;
00023
00024 import java.util.*;
00025 import java.text.*;
00026
00033 public class XYLineGraph extends Canvas implements PaintListener {
00034
00035
00036
00037
00038 public static final int AXIS_LEFT = 0;
00039 public static final int AXIS_RIGHT = 1;
00040 public static final int AXIS_TOP = 2;
00041 public static final int AXIS_BOTTOM = 3;
00042
00043 private static final int MIN_HEIGHT = 30;
00044 private static final int MIN_WIDTH = 30;
00045
00046 private static final int LABEL_GAP_LEFT = 5;
00047 private static final int LABEL_GAP_RIGHT = 4;
00048 private static final int LABEL_GAP_TOP = 3;
00049 private static final int LABEL_GAP_BOTTOM = -5;
00050
00051 private static final int DRAW_AXIS = 1;
00052 private static final int DRAW_TICK = 2;
00053 private static final int DRAW_LABEL = 4;
00054 private static final int DRAW_GRID = 8;
00055 private static final int DRAW_MARKER = 16;
00056
00057 private static final int EPS_SCALE = 20;
00058
00059
00060
00061
00062
00063 private int leftMargin_;
00064 private int rightMargin_;
00065 private int topMargin_;
00066 private int bottomMargin_;
00067
00068 private AxisInfo[] axisInfo_;
00069
00070 private ArrayList<DataSeries> dsList_;
00071 private HashMap<DataSeries, DataSeriesInfo> dsInfoMap_;
00072
00073 private LegendPanel legendPanel_;
00074
00075 private Color backColor_;
00076 private Color borderColor_;
00077
00078
00079 private boolean epsMode_;
00080
00081
00082
00091 public XYLineGraph(
00092 Composite parent,
00093 int leftMargin,
00094 int rightMargin,
00095 int topMargin,
00096 int bottomMargin
00097 ) {
00098 super(parent,SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
00099
00100 leftMargin_ = leftMargin;
00101 rightMargin_ = rightMargin;
00102 topMargin_ = topMargin;
00103 bottomMargin_ = bottomMargin;
00104
00105
00106
00107
00108
00109 backColor_ = Activator.getDefault().getColor("black");
00110 borderColor_ = Activator.getDefault().getColor("black");
00111
00112
00113
00114 axisInfo_ = new AxisInfo[4];
00115 axisInfo_[AXIS_LEFT] = null;
00116 axisInfo_[AXIS_RIGHT] = null;
00117 axisInfo_[AXIS_TOP] = null;
00118 axisInfo_[AXIS_BOTTOM] = null;
00119
00120
00121 dsList_ = new ArrayList<DataSeries>();
00122 dsInfoMap_ = new HashMap<DataSeries, DataSeriesInfo>();
00123
00124
00125 epsMode_ = false;
00126
00127 addPaintListener(this);
00128 }
00129
00130
00131
00137 public void setEPSMode(
00138 boolean flag
00139 ) {
00140 epsMode_ = flag;
00141 }
00142
00148 public void setBackColor(
00149 Color color
00150 ) {
00151 backColor_ = color;
00152 }
00153
00159 public void setBorderColor(
00160 Color color
00161 ) {
00162 borderColor_ = color;
00163 }
00164
00174 public void addDataSeries(
00175 DataSeries ds,
00176 AxisInfo xai,
00177 AxisInfo yai,
00178 RGB rgb,
00179 String legend
00180 ) {
00181 Color color=Activator.getDefault().getColor(rgb);
00182 DataSeriesInfo dsi = new DataSeriesInfo( xai, yai, color, new LegendInfo(color, legend));
00183 legendPanel_.addLegend(dsi.legend);
00184 dsList_.add(ds);
00185 dsInfoMap_.put(ds, dsi);
00186 }
00187
00193 public void removeDataSeries(
00194 DataSeries ds
00195 ) {
00196 int ind = dsList_.indexOf(ds);
00197 dsList_.remove(ind);
00198 DataSeriesInfo dsi = (DataSeriesInfo)dsInfoMap_.get(ds);
00199 legendPanel_.removeLegend(dsi.legend);
00200 dsInfoMap_.remove(ds);
00201 }
00202
00208 public Iterator getDataSeries() {
00209 return (Iterator)dsList_.listIterator();
00210 }
00211
00220 public void setAxisInfo(
00221 int axis,
00222 AxisInfo ai
00223 ) {
00224 axisInfo_[axis] = ai;
00225 }
00226
00234 public AxisInfo getAxisInfo(
00235 int axis
00236 ) {
00237 return axisInfo_[axis];
00238 }
00239
00246 public void setStyle(DataSeries ds, RGB rgb) {
00247 DataSeriesInfo dsi = (DataSeriesInfo)dsInfoMap_.get(ds);
00248 dsi.color = Activator.getDefault().getColor(rgb);
00249 dsi.legend.color = Activator.getDefault().getColor(rgb);
00250 }
00251
00258 public RGB getStyle(DataSeries ds) {
00259 DataSeriesInfo dsi = (DataSeriesInfo)dsInfoMap_.get(ds);
00260 return dsi.color.getRGB();
00261 }
00262
00269 public void setLegendLabel(DataSeries ds, String legend) {
00270 DataSeriesInfo dsi = (DataSeriesInfo)dsInfoMap_.get(ds);
00271 dsi.legend.label = legend;
00272 }
00273
00280 public String getLegendLabel(DataSeries ds) {
00281 DataSeriesInfo dsi = (DataSeriesInfo)dsInfoMap_.get(ds);
00282 return dsi.legend.label;
00283 }
00284
00290 public Canvas getLegendPanel() {
00291 return null;
00292 }
00293
00299 public void paintControl(PaintEvent e) {
00300
00301
00302 int width = getSize().x;
00303 int height = getSize().y;
00304 e.gc.setBackground(backColor_);
00305 e.gc.fillRectangle(0, 0, width, height);
00306 int minWidth = leftMargin_ + MIN_WIDTH + rightMargin_;
00307 int minHeight = topMargin_ + MIN_HEIGHT + bottomMargin_;
00308 if (width < minWidth) {
00309 width = minWidth;
00310 }
00311 if (height < minHeight) {
00312 height = minHeight;
00313 }
00314
00315
00316 int xl = leftMargin_;
00317 int xr = width - rightMargin_ - 1;
00318 int yt = topMargin_;
00319 int yb = height - bottomMargin_ - 1;
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337 int flag = DRAW_GRID;
00338 drawAxis(e.gc, xl, yt, xr, yb, AXIS_LEFT, flag);
00339 drawAxis(e.gc, xl, yt, xr, yb, AXIS_RIGHT, flag);
00340 drawAxis(e.gc, xl, yt, xr, yb, AXIS_TOP, flag);
00341 drawAxis(e.gc, xl, yt, xr, yb, AXIS_BOTTOM, flag);
00342
00343
00344 if (epsMode_) {
00345 e.gc.setClipping(xl, yt, xr - xl, yb - yt);
00346 }
00347
00348
00349
00350
00351 ListIterator li = dsList_.listIterator();
00352 while (li.hasNext()) {
00353 DataSeries ds = (DataSeries)li.next();
00354
00355 DataSeriesInfo dsi = (DataSeriesInfo)dsInfoMap_.get(ds);
00356
00357 double xbase = dsi.xAxisInfo.base;
00358 double ybase = dsi.yAxisInfo.base;
00359 double xscale = (xr - xl) / dsi.xAxisInfo.extent;
00360 double yscale = (yb - yt) / dsi.yAxisInfo.extent;
00361 double factor = dsi.yAxisInfo.factor;
00362
00363 double xOffset = ds.getXOffset();
00364 double xStep = ds.getXStep();
00365 double[] data = ds.getData();
00366 int headPos = ds.getHeadPos();
00367 int length = data.length;
00368 int ox = 0, oy = 0;
00369 int nx = 0, ny = 0;
00370 boolean connect = false;
00371 e.gc.setForeground(dsi.color);
00372 int iofs = - headPos;
00373
00374
00375 for (int i = headPos; i < length; i++) {
00376 if (Double.isNaN(data[i])) {
00377
00378
00379
00380 if (connect) {
00381
00382 e.gc.drawLine(ox, oy, ox, oy);
00383 connect = false;
00384 }
00385 } else {
00386 nx = xl + (int)(((xStep * (i + iofs) + xOffset) - xbase) * xscale);
00387
00388
00389
00390
00391
00392
00393 ny = yb - (int)((data[i] * factor - ybase) * yscale);
00394 if (connect) {
00395
00396 e.gc.drawLine(ox, oy, nx, ny);
00397 ox = nx;
00398 oy = ny;
00399 } else {
00400 ox = nx;
00401 oy = ny;
00402 connect = true;
00403 }
00404 }
00405 }
00406 iofs = length - headPos;
00407 for (int i = 0; i < headPos; i++) {
00408 if (Double.isNaN(data[i])) {
00409 if (connect) {
00410
00411 e.gc.drawLine(ox, oy, ox, oy);
00412 connect = false;
00413 }
00414 } else {
00415 nx = xl + (int)(((xStep * (i + iofs) + xOffset) - xbase) * xscale);
00416 ny = yb - (int)((data[i] * factor - ybase) * yscale);
00417 if (connect) {
00418
00419 e.gc.drawLine(ox, oy, nx, ny);
00420 ox = nx;
00421 oy = ny;
00422 } else {
00423 ox = nx;
00424 oy = ny;
00425 connect = true;
00426 }
00427 }
00428 }
00429
00430
00431 }
00432
00433
00434 if (epsMode_) {
00435
00436
00437
00438 } else {
00439
00440 e.gc.setBackground(borderColor_);
00441 e.gc.fillRectangle(0, 0, xl, height);
00442 e.gc.fillRectangle(xr + 1, 0, width, height);
00443 e.gc.fillRectangle(0, 0, width, yt);
00444 e.gc.fillRectangle(0, yb + 1, width, height);
00445 }
00446
00447
00448 flag = DRAW_AXIS + DRAW_TICK + DRAW_LABEL + DRAW_MARKER;
00449 drawAxis(e.gc, xl, yt, xr, yb, AXIS_LEFT, flag);
00450 drawAxis(e.gc, xl, yt, xr, yb, AXIS_RIGHT, flag);
00451 drawAxis(e.gc, xl, yt, xr, yb, AXIS_TOP, flag);
00452 drawAxis(e.gc, xl, yt, xr, yb, AXIS_BOTTOM, flag);
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464 if (epsMode_) {
00465
00466
00467
00468 }
00469
00470
00471 }
00472
00482 private void drawAxis(
00483 GC gc,
00484
00485
00486 int xl,
00487 int yt,
00488 int xr,
00489 int yb,
00490 int axis,
00491 int flag
00492 ) {
00493
00494
00495
00496
00497
00498
00499
00500 AxisInfo ai = axisInfo_[axis];
00501 if (ai != null) {
00502 int tickLength = ai.tickLength;
00503 int unitXOfs = ai.unitXOfs;
00504 int unitYOfs = ai.unitYOfs;
00505 int ascale = 1;
00506 if (epsMode_) {
00507 tickLength *= EPS_SCALE;
00508 unitXOfs *= EPS_SCALE;
00509 unitYOfs *= EPS_SCALE;
00510 ascale = EPS_SCALE;
00511 }
00512
00513 if ((flag & DRAW_AXIS) != 0) {
00514 gc.setForeground(ai.color);
00515 switch (axis) {
00516 case AXIS_LEFT:
00517 gc.drawLine(xl, yt, xl, yb);
00518 break;
00519 case AXIS_RIGHT:
00520 gc.drawLine(xr, yt, xr, yb);
00521 break;
00522 case AXIS_TOP:
00523 gc.drawLine(xl, yt, xr, yt);
00524 break;
00525 case AXIS_BOTTOM:
00526 gc.drawLine(xl, yb, xr, yb);
00527 break;
00528 }
00529 }
00530 double base = ai.base;
00531 double extent = ai.extent;
00532 double scale = 1;
00533 switch (axis) {
00534 case AXIS_LEFT:
00535 case AXIS_RIGHT:
00536 scale = (yb - yt) / extent;
00537 break;
00538 case AXIS_TOP:
00539 case AXIS_BOTTOM:
00540 scale = (xr - xl) / extent;
00541 break;
00542 }
00543 double min = base;
00544 double max = base + extent;
00545 if (ai.minLimitEnabled && min < ai.min) {
00546 min = ai.min;
00547 }
00548 if (ai.maxLimitEnabled && max > ai.max) {
00549 max = ai.max;
00550 }
00551
00552 double every = ai.tickEvery;
00553 if (every > 0.0 && ((flag & DRAW_TICK) != 0)) {
00554 gc.setForeground(ai.color);
00555 int cfrom = (int)(Math.ceil(min / every));
00556 int cto = (int)(Math.floor(max / every));
00557 int pos;
00558 switch (axis) {
00559 case AXIS_LEFT:
00560 for (int i = cfrom; i <= cto; i ++) {
00561 pos = yb - (int)((every * i - base) * scale);
00562 gc.drawLine(xl, pos, xl - tickLength, pos);
00563 }
00564 break;
00565 case AXIS_RIGHT:
00566 for (int i = cfrom; i <= cto; i ++) {
00567 pos = yb - (int)((every * i - base) * scale);
00568 gc.drawLine(xr, pos, xr + tickLength, pos);
00569 }
00570 break;
00571 case AXIS_TOP:
00572 for (int i = cfrom; i <= cto; i ++) {
00573 pos = xl + (int)((every * i - base) * scale);
00574 gc.drawLine(pos, yt, pos, yt - tickLength);
00575 }
00576 break;
00577 case AXIS_BOTTOM:
00578 for (int i = cfrom; i <= cto; i ++) {
00579 pos = xl + (int)((every * i - base) * scale);
00580 gc.drawLine(pos, yb, pos, yb + tickLength);
00581 }
00582 break;
00583 }
00584 }
00585
00586 every = ai.gridEvery;
00587 if (every > 0.0 && ((flag & DRAW_GRID) != 0)) {
00588 gc.setForeground(ai.gridColor);
00589 int cfrom = (int)(Math.ceil(min / every));
00590 int cto = (int)(Math.floor(max / every));
00591 int pos;
00592 switch (axis) {
00593 case AXIS_LEFT:
00594 case AXIS_RIGHT:
00595 for (int i = cfrom; i <= cto; i ++) {
00596 pos = yb - (int)((every * i - base) * scale);
00597 gc.drawLine(xl + 1, pos, xr - 1, pos);
00598 }
00599 break;
00600 case AXIS_TOP:
00601 case AXIS_BOTTOM:
00602 for (int i = cfrom; i <= cto; i ++) {
00603 pos = xl + (int)((every * i - base) * scale);
00604 gc.drawLine(pos, yt + 1, pos, yb - 1);
00605 }
00606 break;
00607 }
00608 }
00609
00610 every = ai.labelEvery;
00611 if (every > 0.0 && ((flag & DRAW_LABEL) != 0)) {
00612 DecimalFormat lformat = new DecimalFormat(ai.labelFormat);
00613 gc.setForeground(ai.labelColor);
00614 gc.setFont(ai.labelFont);
00615 FontMetrics lmetrics = gc.getFontMetrics();
00616 int cfrom = (int)(Math.ceil(min / every));
00617 int cto = (int)(Math.floor(max / every));
00618 int xpos, ypos;
00619 String lstr;
00620 switch (axis) {
00621 case AXIS_LEFT:
00622 for (int i = cfrom; i <= cto; i ++) {
00623 lstr = lformat.format(every * i);
00624 xpos = xl - tickLength - ascale * LABEL_GAP_LEFT
00625 - ascale * lmetrics.getAverageCharWidth();
00626 ypos = yb - (int)((every * i - base) * scale)
00627 + (int)(ascale * lmetrics.getHeight() / 3.5);
00628 gc.drawString(lstr, xpos, ypos);
00629 }
00630 break;
00631 case AXIS_RIGHT:
00632 for (int i = cfrom; i <= cto; i ++) {
00633 lstr = lformat.format(every * i);
00634 xpos = xr + tickLength + ascale * LABEL_GAP_RIGHT;
00635 ypos = yb - (int)((every * i - base) * scale)
00636 + (int)(ascale * lmetrics.getHeight() / 3.5);
00637 gc.drawString(lstr, xpos, ypos);
00638 }
00639 break;
00640 case AXIS_TOP:
00641 ypos = yt - tickLength - ascale * LABEL_GAP_TOP;
00642 for (int i = cfrom; i <= cto; i ++) {
00643 lstr = lformat.format(every * i);
00644 xpos = xl + (int)((every * i - base) * scale)
00645 - ascale * lmetrics.getAverageCharWidth() / 2;
00646 gc.drawString(lstr, xpos, ypos);
00647 }
00648 break;
00649 case AXIS_BOTTOM:
00650 ypos = yb + tickLength
00651 + ascale * LABEL_GAP_BOTTOM
00652 + ascale * lmetrics.getHeight();
00653 for (int i = cfrom; i <= cto; i ++) {
00654 lstr = lformat.format(every * i);
00655 xpos = xl + (int)((every * i - base) * scale)
00656 - ascale * lmetrics.getAverageCharWidth() / 2;
00657 gc.drawString(lstr, xpos, ypos);
00658 }
00659 break;
00660 }
00661
00662
00663 FontMetrics umetrics = gc.getFontMetrics();
00664 int ux, uy;
00665 gc.setForeground(ai.unitColor);
00666
00667 switch (axis) {
00668 case AXIS_LEFT:
00669 ux = xl - unitXOfs - ascale * umetrics.getAverageCharWidth();
00670 uy = yt - unitYOfs;
00671 gc.drawString(ai.unitLabel, ux, uy);
00672 break;
00673 case AXIS_RIGHT:
00674 ux = xr + unitXOfs;
00675 uy = yt - unitYOfs;
00676 gc.drawString(ai.unitLabel, ux, uy);
00677 break;
00678 case AXIS_TOP:
00679 ux = xr + unitXOfs;
00680 uy = yt - unitYOfs;
00681 gc.drawString(ai.unitLabel, ux, uy);
00682 break;
00683 case AXIS_BOTTOM:
00684 ux = xr + unitXOfs;
00685 uy = yb + unitYOfs + ascale * umetrics.getHeight();
00686 gc.drawString(ai.unitLabel, ux, uy);
00687 break;
00688 }
00689 }
00690
00691 if (ai.markerVisible
00692
00693 && ((flag & DRAW_MARKER) != 0)) {
00694 gc.setForeground(ai.markerColor);
00695 int pos;
00696 switch (axis) {
00697 case AXIS_LEFT:
00698 case AXIS_RIGHT:
00699
00700
00701 pos = yb - (int)(ai.markerPos * (yb - yt));
00702 gc.drawLine(xl + 1, pos - 1, xr - 1, pos - 1);
00703 gc.drawLine(xl + 1, pos, xr - 1, pos);
00704 gc.drawLine(xl + 1, pos + 1, xr - 1, pos + 1);
00705 break;
00706 case AXIS_TOP:
00707 case AXIS_BOTTOM:
00708
00709
00710 pos = xl + (int)(ai.markerPos * (xr - xl));
00711 gc.drawLine(pos - 1, yt + 1, pos - 1, yb - 1);
00712 gc.drawLine(pos, yt + 1, pos, yb - 1);
00713 gc.drawLine(pos + 1, yt + 1, pos + 1, yb - 1);
00714 break;
00715 }
00716
00717 }
00718 }
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737 }
00738
00739
00740
00745 private class DataSeriesInfo {
00746
00747
00748
00749 public AxisInfo xAxisInfo;
00750 public AxisInfo yAxisInfo;
00751 public Color color;
00752 public LegendInfo legend;
00753
00754
00755
00764 public DataSeriesInfo(
00765 AxisInfo xAxisInfo,
00766 AxisInfo yAxisInfo,
00767 Color color,
00768 LegendInfo legend
00769 ) {
00770 this.xAxisInfo = xAxisInfo;
00771 this.yAxisInfo = yAxisInfo;
00772 this.color = color;
00773 this.legend = legend;
00774 }
00775 }
00776
00777 public void setLegend(LegendPanel legend) {
00778 legendPanel_ = legend;
00779 }
00780
00781 }