001 /* ============================================================
002 * JRobin : Pure java implementation of RRDTool's functionality
003 * ============================================================
004 *
005 * Project Info: http://www.jrobin.org
006 * Project Lead: Sasa Markovic (saxon@jrobin.org)
007 *
008 * Developers: Sasa Markovic (saxon@jrobin.org)
009 *
010 *
011 * (C) Copyright 2003-2005, by Sasa Markovic.
012 *
013 * This library is free software; you can redistribute it and/or modify it under the terms
014 * of the GNU Lesser General Public License as published by the Free Software Foundation;
015 * either version 2.1 of the License, or (at your option) any later version.
016 *
017 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
018 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
019 * See the GNU Lesser General Public License for more details.
020 *
021 * You should have received a copy of the GNU Lesser General Public License along with this
022 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
023 * Boston, MA 02111-1307, USA.
024 */
025 package org.jrobin.graph;
026
027 import org.jrobin.core.RrdException;
028 import org.jrobin.core.Util;
029 import org.jrobin.data.DataProcessor;
030
031 import javax.swing.*;
032 import java.awt.*;
033 import java.io.IOException;
034
035 /**
036 * Class which actually creates JRobin graphs (does the hard work).
037 */
038 public class RrdGraph implements RrdGraphConstants {
039 RrdGraphDef gdef;
040 ImageParameters im = new ImageParameters();
041 DataProcessor dproc;
042 ImageWorker worker;
043 Mapper mapper;
044 RrdGraphInfo info = new RrdGraphInfo();
045 private String signature;
046
047 /**
048 * Creates graph from the corresponding {@link RrdGraphDef} object.
049 *
050 * @param gdef Graph definition
051 * @throws IOException Thrown in case of I/O error
052 * @throws RrdException Thrown in case of JRobin related error
053 */
054 public RrdGraph(RrdGraphDef gdef) throws IOException, RrdException {
055 this.gdef = gdef;
056 signature = gdef.getSignature();
057 worker = new ImageWorker(100, 100); // Dummy worker, just to start with something
058 try {
059 createGraph();
060 }
061 finally {
062 worker.dispose();
063 worker = null;
064 dproc = null;
065 }
066 }
067
068 /**
069 * Returns complete graph information in a single object.
070 *
071 * @return Graph information (width, height, filename, image bytes, etc...)
072 */
073 public RrdGraphInfo getRrdGraphInfo() {
074 return info;
075 }
076
077 private void createGraph() throws RrdException, IOException {
078 boolean lazy = lazyCheck();
079 if (!lazy || gdef.printStatementCount() != 0) {
080 fetchData();
081 resolveTextElements();
082 if (gdef.shouldPlot() && !lazy) {
083 calculatePlotValues();
084 findMinMaxValues();
085 identifySiUnit();
086 expandValueRange();
087 removeOutOfRangeRules();
088 initializeLimits();
089 placeLegends();
090 createImageWorker();
091 drawBackground();
092 drawData();
093 drawGrid();
094 drawAxis();
095 drawText();
096 drawLegend();
097 drawRules();
098 gator();
099 drawOverlay();
100 saveImage();
101 }
102 }
103 collectInfo();
104 }
105
106 private void collectInfo() {
107 info.filename = gdef.filename;
108 info.width = im.xgif;
109 info.height = im.ygif;
110 for (CommentText comment : gdef.comments) {
111 if (comment instanceof PrintText) {
112 PrintText pt = (PrintText) comment;
113 if (pt.isPrint()) {
114 info.addPrintLine(pt.resolvedText);
115 }
116 }
117 }
118 if (gdef.imageInfo != null) {
119 info.imgInfo = Util.sprintf(gdef.imageInfo, gdef.filename, im.xgif, im.ygif);
120 }
121 }
122
123 private void saveImage() throws IOException {
124 if (!gdef.filename.equals("-")) {
125 info.bytes = worker.saveImage(gdef.filename, gdef.imageFormat, gdef.imageQuality);
126 }
127 else {
128 info.bytes = worker.getImageBytes(gdef.imageFormat, gdef.imageQuality);
129 }
130 }
131
132 private void drawOverlay() throws IOException {
133 if (gdef.overlayImage != null) {
134 worker.loadImage(gdef.overlayImage);
135 }
136 }
137
138 private void gator() {
139 if (!gdef.onlyGraph && gdef.showSignature) {
140 Font font = gdef.getSmallFont().deriveFont(Font.PLAIN, 9);
141 int x = (int) (im.xgif - 2 - worker.getFontAscent(font));
142 int y = 4;
143 worker.transform(x, y, Math.PI / 2);
144 worker.drawString(signature, 0, 0, font, Color.LIGHT_GRAY);
145 worker.reset();
146 }
147 }
148
149 private void drawRules() {
150 worker.clip(im.xorigin + 1, im.yorigin - gdef.height - 1, gdef.width - 1, gdef.height + 2);
151 for (PlotElement pe : gdef.plotElements) {
152 if (pe instanceof HRule) {
153 HRule hr = (HRule) pe;
154 if (hr.value >= im.minval && hr.value <= im.maxval) {
155 int y = mapper.ytr(hr.value);
156 worker.drawLine(im.xorigin, y, im.xorigin + im.xsize, y, hr.color, new BasicStroke(hr.width));
157 }
158 }
159 else if (pe instanceof VRule) {
160 VRule vr = (VRule) pe;
161 if (vr.timestamp >= im.start && vr.timestamp <= im.end) {
162 int x = mapper.xtr(vr.timestamp);
163 worker.drawLine(x, im.yorigin, x, im.yorigin - im.ysize, vr.color, new BasicStroke(vr.width));
164 }
165 }
166 }
167 worker.reset();
168 }
169
170 private void drawText() {
171 if (!gdef.onlyGraph) {
172 if (gdef.title != null) {
173 int x = im.xgif / 2 - (int) (worker.getStringWidth(gdef.title, gdef.largeFont) / 2);
174 int y = PADDING_TOP + (int) worker.getFontAscent(gdef.largeFont);
175 worker.drawString(gdef.title, x, y, gdef.largeFont, gdef.colors[COLOR_FONT]);
176 }
177 if (gdef.verticalLabel != null) {
178 int x = PADDING_LEFT;
179 int y = im.yorigin - im.ysize / 2 + (int) worker.getStringWidth(gdef.verticalLabel, gdef.getSmallFont()) / 2;
180 int ascent = (int) worker.getFontAscent(gdef.smallFont);
181 worker.transform(x, y, -Math.PI / 2);
182 worker.drawString(gdef.verticalLabel, 0, ascent, gdef.smallFont, gdef.colors[COLOR_FONT]);
183 worker.reset();
184 }
185 }
186 }
187
188 private void drawGrid() {
189 if (!gdef.onlyGraph) {
190 Paint shade1 = gdef.colors[COLOR_SHADEA], shade2 = gdef.colors[COLOR_SHADEB];
191 Stroke borderStroke = new BasicStroke(1);
192 worker.drawLine(0, 0, im.xgif - 1, 0, shade1, borderStroke);
193 worker.drawLine(1, 1, im.xgif - 2, 1, shade1, borderStroke);
194 worker.drawLine(0, 0, 0, im.ygif - 1, shade1, borderStroke);
195 worker.drawLine(1, 1, 1, im.ygif - 2, shade1, borderStroke);
196 worker.drawLine(im.xgif - 1, 0, im.xgif - 1, im.ygif - 1, shade2, borderStroke);
197 worker.drawLine(0, im.ygif - 1, im.xgif - 1, im.ygif - 1, shade2, borderStroke);
198 worker.drawLine(im.xgif - 2, 1, im.xgif - 2, im.ygif - 2, shade2, borderStroke);
199 worker.drawLine(1, im.ygif - 2, im.xgif - 2, im.ygif - 2, shade2, borderStroke);
200 if (gdef.drawXGrid) {
201 new TimeAxis(this).draw();
202 }
203 if (gdef.drawYGrid) {
204 boolean ok;
205 if (gdef.altYMrtg) {
206 ok = new ValueAxisMrtg(this).draw();
207 }
208 else if (gdef.logarithmic) {
209 ok = new ValueAxisLogarithmic(this).draw();
210 }
211 else {
212 ok = new ValueAxis(this).draw();
213 }
214 if (!ok) {
215 String msg = "No Data Found";
216 worker.drawString(msg,
217 im.xgif / 2 - (int) worker.getStringWidth(msg, gdef.largeFont) / 2,
218 (2 * im.yorigin - im.ysize) / 2,
219 gdef.largeFont, gdef.colors[COLOR_FONT]);
220 }
221 }
222 }
223 }
224
225 private void drawData() throws RrdException {
226 worker.setAntiAliasing(gdef.antiAliasing);
227 worker.clip(im.xorigin + 1, im.yorigin - gdef.height - 1, gdef.width - 1, gdef.height + 2);
228 double areazero = mapper.ytr((im.minval > 0.0) ? im.minval : (im.maxval < 0.0) ? im.maxval : 0.0);
229 double[] x = xtr(dproc.getTimestamps()), lastY = null;
230 // draw line, area and stack
231 for (PlotElement plotElement : gdef.plotElements) {
232 if (plotElement instanceof SourcedPlotElement) {
233 SourcedPlotElement source = (SourcedPlotElement) plotElement;
234 double[] y = ytr(source.getValues());
235 if (source instanceof Line) {
236 worker.drawPolyline(x, y, source.color, new BasicStroke(((Line) source).width));
237 }
238 else if (source instanceof Area) {
239 worker.fillPolygon(x, areazero, y, source.color);
240 }
241 else if (source instanceof Stack) {
242 Stack stack = (Stack) source;
243 float width = stack.getParentLineWidth();
244 if (width >= 0F) {
245 // line
246 worker.drawPolyline(x, y, stack.color, new BasicStroke(width));
247 }
248 else {
249 // area
250 worker.fillPolygon(x, lastY, y, stack.color);
251 worker.drawPolyline(x, lastY, stack.getParentColor(), new BasicStroke(0));
252 }
253 }
254 else {
255 // should not be here
256 throw new RrdException("Unknown plot source: " + source.getClass().getName());
257 }
258 lastY = y;
259 }
260 }
261 worker.reset();
262 worker.setAntiAliasing(false);
263 }
264
265 private void drawAxis() {
266 if (!gdef.onlyGraph) {
267 Paint gridColor = gdef.colors[COLOR_GRID];
268 Paint fontColor = gdef.colors[COLOR_FONT];
269 Paint arrowColor = gdef.colors[COLOR_ARROW];
270 Stroke stroke = new BasicStroke(1);
271 worker.drawLine(im.xorigin + im.xsize, im.yorigin, im.xorigin + im.xsize, im.yorigin - im.ysize,
272 gridColor, stroke);
273 worker.drawLine(im.xorigin, im.yorigin - im.ysize, im.xorigin + im.xsize, im.yorigin - im.ysize,
274 gridColor, stroke);
275 worker.drawLine(im.xorigin - 4, im.yorigin, im.xorigin + im.xsize + 4, im.yorigin,
276 fontColor, stroke);
277 worker.drawLine(im.xorigin, im.yorigin, im.xorigin, im.yorigin - im.ysize,
278 gridColor, stroke);
279 worker.drawLine(im.xorigin + im.xsize + 4, im.yorigin - 3, im.xorigin + im.xsize + 4, im.yorigin + 3,
280 arrowColor, stroke);
281 worker.drawLine(im.xorigin + im.xsize + 4, im.yorigin - 3, im.xorigin + im.xsize + 9, im.yorigin,
282 arrowColor, stroke);
283 worker.drawLine(im.xorigin + im.xsize + 4, im.yorigin + 3, im.xorigin + im.xsize + 9, im.yorigin,
284 arrowColor, stroke);
285 }
286 }
287
288 private void drawBackground() throws IOException {
289 worker.fillRect(0, 0, im.xgif, im.ygif, gdef.colors[COLOR_BACK]);
290 if (gdef.backgroundImage != null) {
291 worker.loadImage(gdef.backgroundImage);
292 }
293 worker.fillRect(im.xorigin, im.yorigin - im.ysize, im.xsize, im.ysize, gdef.colors[COLOR_CANVAS]);
294 }
295
296 private void createImageWorker() {
297 worker.resize(im.xgif, im.ygif);
298 }
299
300 private void placeLegends() {
301 if (!gdef.noLegend && !gdef.onlyGraph) {
302 int border = (int) (getSmallFontCharWidth() * PADDING_LEGEND);
303 LegendComposer lc = new LegendComposer(this, border, im.ygif, im.xgif - 2 * border);
304 im.ygif = lc.placeComments() + PADDING_BOTTOM;
305 }
306 }
307
308 private void initializeLimits() throws RrdException {
309 im.xsize = gdef.width;
310 im.ysize = gdef.height;
311 im.unitslength = gdef.unitsLength;
312 if (gdef.onlyGraph) {
313 if (im.ysize > 64) {
314 throw new RrdException("Cannot create graph only, height too big");
315 }
316 im.xorigin = 0;
317 }
318 else {
319 im.xorigin = (int) (PADDING_LEFT + im.unitslength * getSmallFontCharWidth());
320 }
321 if (gdef.verticalLabel != null) {
322 im.xorigin += getSmallFontHeight();
323 }
324 if (gdef.onlyGraph) {
325 im.yorigin = im.ysize;
326 }
327 else {
328 im.yorigin = PADDING_TOP + im.ysize;
329 }
330 mapper = new Mapper(this);
331 if (gdef.title != null) {
332 im.yorigin += getLargeFontHeight() + PADDING_TITLE;
333 }
334 if (gdef.onlyGraph) {
335 im.xgif = im.xsize;
336 im.ygif = im.yorigin;
337 }
338 else {
339 im.xgif = PADDING_RIGHT + im.xsize + im.xorigin;
340 im.ygif = im.yorigin + (int) (PADDING_PLOT * getSmallFontHeight());
341 }
342 }
343
344 private void removeOutOfRangeRules() {
345 for (PlotElement plotElement : gdef.plotElements) {
346 if (plotElement instanceof HRule) {
347 ((HRule) plotElement).setLegendVisibility(im.minval, im.maxval, gdef.forceRulesLegend);
348 }
349 else if (plotElement instanceof VRule) {
350 ((VRule) plotElement).setLegendVisibility(im.start, im.end, gdef.forceRulesLegend);
351 }
352 }
353 }
354
355 private void expandValueRange() {
356 im.ygridstep = (gdef.valueAxisSetting != null) ? gdef.valueAxisSetting.gridStep : Double.NaN;
357 im.ylabfact = (gdef.valueAxisSetting != null) ? gdef.valueAxisSetting.labelFactor : 0;
358 if (!gdef.rigid && !gdef.logarithmic) {
359 double sensiblevalues[] = {
360 1000.0, 900.0, 800.0, 750.0, 700.0, 600.0, 500.0, 400.0, 300.0, 250.0, 200.0, 125.0, 100.0,
361 90.0, 80.0, 75.0, 70.0, 60.0, 50.0, 40.0, 30.0, 25.0, 20.0, 10.0,
362 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.5, 3.0, 2.5, 2.0, 1.8, 1.5, 1.2, 1.0,
363 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0, -1
364 };
365 double scaled_min, scaled_max, adj;
366 if (Double.isNaN(im.ygridstep)) {
367 if (gdef.altYMrtg) { /* mrtg */
368 im.decimals = Math.ceil(Math.log10(Math.max(Math.abs(im.maxval), Math.abs(im.minval))));
369 im.quadrant = 0;
370 if (im.minval < 0) {
371 im.quadrant = 2;
372 if (im.maxval <= 0) {
373 im.quadrant = 4;
374 }
375 }
376 switch (im.quadrant) {
377 case 2:
378 im.scaledstep = Math.ceil(50 * Math.pow(10, -(im.decimals)) * Math.max(Math.abs(im.maxval),
379 Math.abs(im.minval))) * Math.pow(10, im.decimals - 2);
380 scaled_min = -2 * im.scaledstep;
381 scaled_max = 2 * im.scaledstep;
382 break;
383 case 4:
384 im.scaledstep = Math.ceil(25 * Math.pow(10,
385 -(im.decimals)) * Math.abs(im.minval)) * Math.pow(10, im.decimals - 2);
386 scaled_min = -4 * im.scaledstep;
387 scaled_max = 0;
388 break;
389 default: /* quadrant 0 */
390 im.scaledstep = Math.ceil(25 * Math.pow(10, -(im.decimals)) * im.maxval) *
391 Math.pow(10, im.decimals - 2);
392 scaled_min = 0;
393 scaled_max = 4 * im.scaledstep;
394 break;
395 }
396 im.minval = scaled_min;
397 im.maxval = scaled_max;
398 }
399 else if (gdef.altAutoscale) {
400 /* measure the amplitude of the function. Make sure that
401 graph boundaries are slightly higher then max/min vals
402 so we can see amplitude on the graph */
403 double delt, fact;
404
405 delt = im.maxval - im.minval;
406 adj = delt * 0.1;
407 fact = 2.0 * Math.pow(10.0,
408 Math.floor(Math.log10(Math.max(Math.abs(im.minval), Math.abs(im.maxval)))) - 2);
409 if (delt < fact) {
410 adj = (fact - delt) * 0.55;
411 }
412 im.minval -= adj;
413 im.maxval += adj;
414 }
415 else if (gdef.altAutoscaleMax) {
416 /* measure the amplitude of the function. Make sure that
417 graph boundaries are slightly higher than max vals
418 so we can see amplitude on the graph */
419 adj = (im.maxval - im.minval) * 0.1;
420 im.maxval += adj;
421 }
422 else {
423 scaled_min = im.minval / im.magfact;
424 scaled_max = im.maxval / im.magfact;
425 for (int i = 1; sensiblevalues[i] > 0; i++) {
426 if (sensiblevalues[i - 1] >= scaled_min && sensiblevalues[i] <= scaled_min) {
427 im.minval = sensiblevalues[i] * im.magfact;
428 }
429 if (-sensiblevalues[i - 1] <= scaled_min && -sensiblevalues[i] >= scaled_min) {
430 im.minval = -sensiblevalues[i - 1] * im.magfact;
431 }
432 if (sensiblevalues[i - 1] >= scaled_max && sensiblevalues[i] <= scaled_max) {
433 im.maxval = sensiblevalues[i - 1] * im.magfact;
434 }
435 if (-sensiblevalues[i - 1] <= scaled_max && -sensiblevalues[i] >= scaled_max) {
436 im.maxval = -sensiblevalues[i] * im.magfact;
437 }
438 }
439 }
440 }
441 else {
442 im.minval = (double) im.ylabfact * im.ygridstep *
443 Math.floor(im.minval / ((double) im.ylabfact * im.ygridstep));
444 im.maxval = (double) im.ylabfact * im.ygridstep *
445 Math.ceil(im.maxval / ((double) im.ylabfact * im.ygridstep));
446 }
447
448 }
449 }
450
451 private void identifySiUnit() {
452 im.unitsexponent = gdef.unitsExponent;
453 im.base = gdef.base;
454 if (!gdef.logarithmic) {
455 final char symbol[] = {'a', 'f', 'p', 'n', 'u', 'm', ' ', 'k', 'M', 'G', 'T', 'P', 'E'};
456 int symbcenter = 6;
457 double digits;
458 if (im.unitsexponent != Integer.MAX_VALUE) {
459 digits = Math.floor(im.unitsexponent / 3);
460 }
461 else {
462 digits = Math.floor(Math.log(Math.max(Math.abs(im.minval), Math.abs(im.maxval))) / Math.log(im.base));
463 }
464 im.magfact = Math.pow(im.base, digits);
465 if (((digits + symbcenter) < symbol.length) && ((digits + symbcenter) >= 0)) {
466 im.symbol = symbol[(int) digits + symbcenter];
467 }
468 else {
469 im.symbol = '?';
470 }
471 }
472 }
473
474 private void findMinMaxValues() {
475 double minval = Double.NaN, maxval = Double.NaN;
476 for (PlotElement pe : gdef.plotElements) {
477 if (pe instanceof SourcedPlotElement) {
478 minval = Util.min(((SourcedPlotElement) pe).getMinValue(), minval);
479 maxval = Util.max(((SourcedPlotElement) pe).getMaxValue(), maxval);
480 }
481 }
482 if (Double.isNaN(minval)) {
483 minval = 0D;
484 }
485 if (Double.isNaN(maxval)) {
486 maxval = 1D;
487 }
488 im.minval = gdef.minValue;
489 im.maxval = gdef.maxValue;
490 /* adjust min and max values */
491 if (Double.isNaN(im.minval) || ((!gdef.logarithmic && !gdef.rigid) && im.minval > minval)) {
492 im.minval = minval;
493 }
494 if (Double.isNaN(im.maxval) || (!gdef.rigid && im.maxval < maxval)) {
495 if (gdef.logarithmic) {
496 im.maxval = maxval * 1.1;
497 }
498 else {
499 im.maxval = maxval;
500 }
501 }
502 /* make sure min is smaller than max */
503 if (im.minval > im.maxval) {
504 im.minval = 0.99 * im.maxval;
505 }
506 /* make sure min and max are not equal */
507 if (im.minval == im.maxval) {
508 im.maxval *= 1.01;
509 if (!gdef.logarithmic) {
510 im.minval *= 0.99;
511 }
512 /* make sure min and max are not both zero */
513 if (im.maxval == 0.0) {
514 im.maxval = 1.0;
515 }
516 }
517 }
518
519 private void calculatePlotValues() throws RrdException {
520 for (PlotElement pe : gdef.plotElements) {
521 if (pe instanceof SourcedPlotElement) {
522 ((SourcedPlotElement) pe).assignValues(dproc);
523 }
524 }
525 }
526
527 private void resolveTextElements() throws RrdException {
528 ValueScaler valueScaler = new ValueScaler(gdef.base);
529 for (CommentText comment : gdef.comments) {
530 comment.resolveText(dproc, valueScaler);
531 }
532 }
533
534 private void fetchData() throws RrdException, IOException {
535 dproc = new DataProcessor(gdef.startTime, gdef.endTime);
536 dproc.setPoolUsed(gdef.poolUsed);
537 if (gdef.step > 0) {
538 dproc.setStep(gdef.step);
539 }
540 for (Source src : gdef.sources) {
541 src.requestData(dproc);
542 }
543 dproc.processData();
544 //long[] t = dproc.getTimestamps();
545 //im.start = t[0];
546 //im.end = t[t.length - 1];
547 im.start = gdef.startTime;
548 im.end = gdef.endTime;
549 }
550
551 private boolean lazyCheck() {
552 // redraw if lazy option is not set or file does not exist
553 if (!gdef.lazy || !Util.fileExists(gdef.filename)) {
554 return false; // 'false' means 'redraw'
555 }
556 // redraw if not enough time has passed
557 long secPerPixel = (gdef.endTime - gdef.startTime) / gdef.width;
558 long elapsed = Util.getTimestamp() - Util.getLastModified(gdef.filename);
559 return elapsed <= secPerPixel;
560 }
561
562 private void drawLegend() {
563 if (!gdef.onlyGraph && !gdef.noLegend) {
564 int ascent = (int) worker.getFontAscent(gdef.smallFont);
565 int box = (int) getBox(), boxSpace = (int) (getBoxSpace());
566 for (CommentText c : gdef.comments) {
567 if (c.isValidGraphElement()) {
568 int x = c.x, y = c.y + ascent;
569 if (c instanceof LegendText) {
570 // draw with BOX
571 worker.fillRect(x, y - box, box, box, gdef.colors[COLOR_FRAME]);
572 worker.fillRect(x + 1, y - box + 1, box - 2, box - 2, ((LegendText) c).legendColor);
573 worker.drawString(c.resolvedText, x + boxSpace, y, gdef.smallFont, gdef.colors[COLOR_FONT]);
574 }
575 else {
576 worker.drawString(c.resolvedText, x, y, gdef.smallFont, gdef.colors[COLOR_FONT]);
577 }
578 }
579 }
580 }
581 }
582
583 // helper methods
584
585 double getSmallFontHeight() {
586 return worker.getFontHeight(gdef.smallFont);
587 }
588
589 private double getLargeFontHeight() {
590 return worker.getFontHeight(gdef.largeFont);
591 }
592
593 private double getSmallFontCharWidth() {
594 return worker.getStringWidth("a", gdef.smallFont);
595 }
596
597 double getInterlegendSpace() {
598 return getSmallFontCharWidth() * LEGEND_INTERSPACING;
599 }
600
601 double getLeading() {
602 return getSmallFontHeight() * LEGEND_LEADING;
603 }
604
605 double getSmallLeading() {
606 return getSmallFontHeight() * LEGEND_LEADING_SMALL;
607 }
608
609 double getBoxSpace() {
610 return Math.ceil(getSmallFontHeight() * LEGEND_BOX_SPACE);
611 }
612
613 private double getBox() {
614 return getSmallFontHeight() * LEGEND_BOX;
615 }
616
617 double[] xtr(long[] timestamps) {
618 /*
619 double[] timestampsDev = new double[timestamps.length];
620 for (int i = 0; i < timestamps.length; i++) {
621 timestampsDev[i] = mapper.xtr(timestamps[i]);
622 }
623 return timestampsDev;
624 */
625 double[] timestampsDev = new double[2 * timestamps.length - 1];
626 for (int i = 0, j = 0; i < timestamps.length; i += 1, j += 2) {
627 timestampsDev[j] = mapper.xtr(timestamps[i]);
628 if (i < timestamps.length - 1) {
629 timestampsDev[j + 1] = timestampsDev[j];
630 }
631 }
632 return timestampsDev;
633 }
634
635 double[] ytr(double[] values) {
636 /*
637 double[] valuesDev = new double[values.length];
638 for (int i = 0; i < values.length; i++) {
639 if (Double.isNaN(values[i])) {
640 valuesDev[i] = Double.NaN;
641 }
642 else {
643 valuesDev[i] = mapper.ytr(values[i]);
644 }
645 }
646 return valuesDev;
647 */
648 double[] valuesDev = new double[2 * values.length - 1];
649 for (int i = 0, j = 0; i < values.length; i += 1, j += 2) {
650 if (Double.isNaN(values[i])) {
651 valuesDev[j] = Double.NaN;
652 }
653 else {
654 valuesDev[j] = mapper.ytr(values[i]);
655 }
656 if (j > 0) {
657 valuesDev[j - 1] = valuesDev[j];
658 }
659 }
660 return valuesDev;
661 }
662
663 /**
664 * Renders this graph onto graphing device
665 *
666 * @param g Graphics handle
667 */
668 public void render(Graphics g) {
669 byte[] imageData = getRrdGraphInfo().getBytes();
670 ImageIcon image = new ImageIcon(imageData);
671 image.paintIcon(null, g, 0, 0);
672 }
673 }