📄 graph.java
字号:
/*
* Copyright (c) 2006 Intel Corporation
* All rights reserved.
*
* This file is distributed under the terms in the attached INTEL-LICENSE
* file. If you do not find these files, copies can be found by writing to
* Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA,
* 94704. Attention: Intel License Inquiry.
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.util.*;
/* Panel for drawing mote-data graphs */
class Graph extends JPanel
{
final static int BORDER_LEFT = 40;
final static int BORDER_RIGHT = 0;
final static int BORDER_TOP = 10;
final static int BORDER_BOTTOM = 10;
final static int TICK_SPACING = 40;
final static int MAX_TICKS = 16;
final static int TICK_WIDTH = 10;
final static int MIN_WIDTH = 50;
int gx0, gx1, gy0, gy1; // graph bounds
int scale = 2; // gx1 - gx0 == MIN_WIDTH << scale
Window parent;
/* Graph to screen coordinate conversion support */
int height, width;
double xscale, yscale;
void updateConversion() {
height = getHeight() - BORDER_TOP - BORDER_BOTTOM;
width = getWidth() - BORDER_LEFT - BORDER_RIGHT;
if (height < 1) {
height = 1;
}
if (width < 1) {
width = 1;
}
xscale = (double)width / (gx1 - gx0 + 1);
yscale = (double)height / (gy1 - gy0 + 1);
}
Graphics makeClip(Graphics g) {
return g.create(BORDER_LEFT, BORDER_TOP, width, height);
}
// Note that these do not include the border offset!
int screenX(int gx) {
return (int)(xscale * (gx - gx0) + 0.5);
}
int screenY(int gy) {
return (int)(height - yscale * (gy - gy0));
}
int graphX(int sx) {
return (int)(sx / xscale + gx0 + 0.5);
}
Graph(Window parent) {
this.parent = parent;
gy0 = 0; gy1 = 0xffff;
gx0 = 0; gx1 = MIN_WIDTH << scale;
}
void rightDrawString(
Graphics2D g,
String s,
int x,
int y) {
TextLayout layout =
new TextLayout(s, parent.smallFont, g.getFontRenderContext());
Rectangle2D bounds = layout.getBounds();
layout.draw(g, x - (float)bounds.getWidth(), y + (float)bounds.getHeight() / 2);
}
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
/* Repaint. Synchronize on Oscilloscope to avoid data changing.
Simply clear panel, draw Y axis and all the mote graphs. */
synchronized (parent.parent) {
updateConversion();
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
drawYAxis(g2d);
Graphics clipped = makeClip(g2d);
int count = parent.moteListModel.size();
for (int i = 0; i < count; i++) {
clipped.setColor(parent.moteListModel.getColor(i));
drawGraph(clipped, parent.moteListModel.get(i));
}
}
}
/* Draw the Y-axis */
protected void drawYAxis(Graphics2D g) {
int axis_x = BORDER_LEFT - 1;
int height = getHeight() - BORDER_BOTTOM - BORDER_TOP;
g.setColor(Color.WHITE);
g.drawLine(axis_x, BORDER_TOP, axis_x, BORDER_TOP + height - 1);
/* Draw a reasonable set of tick marks */
int nTicks = height / TICK_SPACING;
if (nTicks > MAX_TICKS) {
nTicks = MAX_TICKS;
}
int tickInterval = (gy1 - gy0 + 1) / nTicks;
if (tickInterval == 0) {
tickInterval = 1;
}
/* Tick interval should be of the family A * 10^B,
where A = 1, 2 * or 5. We tend more to rounding A up, to reduce
rather than increase the number of ticks. */
int B = (int)(Math.log(tickInterval) / Math.log(10));
int A = (int)(tickInterval / Math.pow(10, B) + 0.5);
if (A > 2) {
A = 5;
} else if (A > 5) {
A = 10;
}
tickInterval = A * (int)Math.pow(10, B);
/* Ticks are printed at multiples of tickInterval */
int tick = ((gy0 + tickInterval - 1) / tickInterval) * tickInterval;
while (tick <= gy1) {
int stick = screenY(tick) + BORDER_TOP;
rightDrawString(g, "" + tick, axis_x - TICK_WIDTH / 2 - 2, stick);
g.drawLine(axis_x - TICK_WIDTH / 2, stick,
axis_x - TICK_WIDTH / 2 + TICK_WIDTH, stick);
tick += tickInterval;
}
}
/* Draw graph for mote nodeId */
protected void drawGraph(Graphics g, int nodeId) {
SingleGraph sg = new SingleGraph(g, nodeId);
if (gx1 - gx0 >= width) {
for (int sx = 0; sx < width; sx++)
sg.nextPoint(g, graphX(sx), sx);
} else {
for (int gx = gx0; gx <= gx1; gx++)
sg.nextPoint(g, gx, screenX(gx));
}
}
/* Inner class to simplify drawing a graph. Simplify initialise it, then
feed it the X screen and graph coordinates, from left to right. */
private class SingleGraph {
int lastsx, lastsy, nodeId;
/* Start drawing the graph mote id */
SingleGraph(Graphics g, int id) {
nodeId = id;
lastsx = -1;
lastsy = -1;
}
/* Next point in mote's graph is at x value gx, screen coordinate sx */
void nextPoint(Graphics g, int gx, int sx) {
int gy = parent.parent.data.getData(nodeId, gx);
int sy = -1;
if (gy >= 0) { // Ignore missing values
double rsy = height - yscale * (gy - gy0);
// Ignore problem values
if (rsy >= -1e6 && rsy <= 1e6) {
sy = (int)(rsy + 0.5);
}
if (lastsy >= 0 && sy >= 0) {
g.drawLine(lastsx, lastsy, sx, sy);
}
}
lastsx = sx;
lastsy = sy;
}
}
/* Update X-axis range in GUI */
void updateXLabel() {
parent.xLabel.setText("X: " + gx0 + " - " + gx1);
}
/* Ensure that graph is nicely positioned on screen. max is the largest
sample number received from any mote. */
private void recenter(int max) {
// New data will show up at the 3/4 point
// The 2nd term ensures that gx1 will be >= max
int scrollby = ((gx1 - gx0) >> 2) + (max - gx1);
gx0 += scrollby;
gx1 += scrollby;
if (gx0 < 0) { // don't bother showing negative sample numbers
gx1 -= gx0;
gx0 = 0;
}
updateXLabel();
}
/* New data received. Redraw graph, scrolling if necessary */
void newData() {
int max = parent.parent.data.maxX();
if (max > gx1 || max < gx0) {
recenter(max);
}
repaint();
}
/* User set the X-axis scale to newScale */
void setScale(int newScale) {
gx1 = gx0 + (MIN_WIDTH << newScale);
scale = newScale;
recenter(parent.parent.data.maxX());
repaint();
}
/* User attempted to set Y-axis range to newy0..newy1. Refuse bogus
values (return false), or accept, redraw and return true. */
boolean setYAxis(int newy0, int newy1) {
if (newy0 >= newy1 || newy0 < 0 || newy0 > 65535 ||
newy1 < 0 || newy1 > 65535) {
return false;
}
gy0 = newy0;
gy1 = newy1;
repaint();
return true;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -