|
5 | 5 | import com.lowagie.text.DocumentException; |
6 | 6 |
|
7 | 7 | import org.jfree.chart.ChartFactory; |
| 8 | +import org.jfree.chart.ChartPanel; |
8 | 9 | import org.jfree.chart.JFreeChart; |
| 10 | +import org.jfree.chart.block.BlockBorder; |
9 | 11 | import org.jfree.chart.labels.StandardXYItemLabelGenerator; |
| 12 | +import org.jfree.chart.plot.PlotOrientation; |
10 | 13 | import org.jfree.chart.plot.XYPlot; |
| 14 | +import org.jfree.chart.renderer.xy.XYDifferenceRenderer; |
| 15 | +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; |
| 16 | +import org.jfree.chart.ui.RectangleEdge; |
11 | 17 | import org.jfree.data.xy.XYDataset; |
12 | 18 | import org.jfree.data.xy.XYSeries; |
13 | 19 | import org.jfree.data.xy.XYSeriesCollection; |
|
16 | 22 | import org.apache.logging.log4j.Logger; |
17 | 23 |
|
18 | 24 | import javax.imageio.ImageIO; |
| 25 | +import javax.swing.*; |
19 | 26 | import java.awt.*; |
| 27 | +import java.awt.geom.Path2D; |
20 | 28 | import java.awt.image.BufferedImage; |
21 | 29 | import java.awt.print.PageFormat; |
22 | 30 | import java.awt.print.Paper; |
@@ -319,4 +327,246 @@ private static PageFormat generateAlternatePageFormat(){ |
319 | 327 | pageFormat.setOrientation(PageFormat.LANDSCAPE); |
320 | 328 | return pageFormat; |
321 | 329 | } |
| 330 | + |
| 331 | + |
| 332 | + public static void main(String[] args) { |
| 333 | + SwingUtilities.invokeLater(() -> { |
| 334 | + |
| 335 | + Random rand = new Random(); |
| 336 | + |
| 337 | + int n = 50; |
| 338 | + double xMin = 0.0; |
| 339 | + double xMax = 7.0; |
| 340 | + double dx = (xMax - xMin) / (n - 1); |
| 341 | + |
| 342 | + double xLower = 0.0; // these are the limits for the x values of the axis |
| 343 | + double xUpper = 7.5; |
| 344 | + |
| 345 | + Color sinColor = new Color(31, 119, 180); // blue |
| 346 | + Color tanColor = new Color(255, 127, 14); // orange |
| 347 | + |
| 348 | + // --- SIN series --- |
| 349 | + XYSeries sinMin = new XYSeries("sin-min"); |
| 350 | + XYSeries sinMax = new XYSeries("sin-max"); |
| 351 | + XYSeries sinMain = new XYSeries("sin"); |
| 352 | + XYSeries sinStd = new XYSeries("sin-std"); |
| 353 | + |
| 354 | + for (int i = 0; i < n; i++) { |
| 355 | + double x = xMin + i * dx; |
| 356 | + double y = Math.sin(x); |
| 357 | + |
| 358 | + double delta = 0.2 + rand.nextDouble() * 0.2; |
| 359 | + double yMin = y - delta; |
| 360 | + double yMax = y + delta; |
| 361 | + |
| 362 | + double std = 0.08 + rand.nextDouble() * 0.08; |
| 363 | + |
| 364 | + sinMin.add(x, yMin); |
| 365 | + sinMax.add(x, yMax); |
| 366 | + sinMain.add(x, y); |
| 367 | + sinStd.add(x, y + std); |
| 368 | + } |
| 369 | + |
| 370 | + // --- TAN series --- |
| 371 | + XYSeries tanMin = new XYSeries("tan-min"); |
| 372 | + XYSeries tanMax = new XYSeries("tan-max"); |
| 373 | + XYSeries tanMain = new XYSeries("tan"); |
| 374 | + XYSeries tanStd = new XYSeries("tan-std"); |
| 375 | + |
| 376 | + for (int i = 0; i < n; i++) { |
| 377 | + double x = xMin + i * dx; |
| 378 | + double y = Math.tan(x); |
| 379 | + |
| 380 | + if (y > 3) y = 3; |
| 381 | + if (y < -3) y = -3; |
| 382 | + |
| 383 | + double delta = 0.3 + rand.nextDouble() * 0.3; |
| 384 | + double yMin = y - delta; |
| 385 | + double yMax = y + delta; |
| 386 | + |
| 387 | + double std = 0.2 + rand.nextDouble() * 0.2; |
| 388 | + |
| 389 | + tanMin.add(x, yMin); |
| 390 | + tanMax.add(x, yMax); |
| 391 | + tanMain.add(x, y); |
| 392 | + tanStd.add(x, y + std); |
| 393 | + } |
| 394 | + |
| 395 | + double globalMin = Double.POSITIVE_INFINITY; |
| 396 | + double globalMax = Double.NEGATIVE_INFINITY; |
| 397 | + for (int i = 0; i < n; i++) { |
| 398 | + globalMin = Math.min(globalMin, sinMin.getY(i).doubleValue()); |
| 399 | + globalMin = Math.min(globalMin, tanMin.getY(i).doubleValue()); |
| 400 | + globalMax = Math.max(globalMax, sinMax.getY(i).doubleValue()); |
| 401 | + globalMax = Math.max(globalMax, tanMax.getY(i).doubleValue()); |
| 402 | + } |
| 403 | + // Add padding |
| 404 | + double pad = 0.1 * (globalMax - globalMin); |
| 405 | + globalMin -= pad; |
| 406 | + globalMax += pad; |
| 407 | + |
| 408 | + // --- Datasets --- |
| 409 | + |
| 410 | + // Dataset 0: sin min/max (for band) |
| 411 | + XYSeriesCollection sinMinMaxDataset = new XYSeriesCollection(); |
| 412 | + sinMinMaxDataset.addSeries(sinMax); // upper |
| 413 | + sinMinMaxDataset.addSeries(sinMin); // lower |
| 414 | + |
| 415 | + // Dataset 1: tan min/max (for band) |
| 416 | + XYSeriesCollection tanMinMaxDataset = new XYSeriesCollection(); |
| 417 | + tanMinMaxDataset.addSeries(tanMax); // upper |
| 418 | + tanMinMaxDataset.addSeries(tanMin); // lower |
| 419 | + |
| 420 | + // Dataset 2: main curves |
| 421 | + XYSeriesCollection mainDataset = new XYSeriesCollection(); |
| 422 | + mainDataset.addSeries(sinMain); |
| 423 | + mainDataset.addSeries(tanMain); |
| 424 | + |
| 425 | + // Dataset 3: std diamonds |
| 426 | + XYSeriesCollection stdDataset = new XYSeriesCollection(); |
| 427 | + stdDataset.addSeries(sinStd); |
| 428 | + stdDataset.addSeries(tanStd); |
| 429 | + |
| 430 | + // --- Chart skeleton --- |
| 431 | + JFreeChart chart = ChartFactory.createXYLineChart( |
| 432 | + "Min/Max Bands + STD Demo", |
| 433 | + "x", |
| 434 | + "y", |
| 435 | + null, |
| 436 | + PlotOrientation.VERTICAL, |
| 437 | + true, |
| 438 | + true, |
| 439 | + false |
| 440 | + ); |
| 441 | + XYPlot plot = chart.getXYPlot(); |
| 442 | + |
| 443 | + // Transparent backgrounds |
| 444 | + chart.setBackgroundPaint(Color.WHITE); |
| 445 | + plot.setBackgroundPaint(Color.WHITE); |
| 446 | + plot.setOutlinePaint(null); |
| 447 | + plot.setDomainGridlinePaint(new Color(180, 180, 180)); // very light |
| 448 | + plot.setRangeGridlinePaint(new Color(180, 180, 180)); |
| 449 | + |
| 450 | + plot.getDomainAxis().setAutoRange(false); // lock the axis so that they never resize |
| 451 | + plot.getRangeAxis().setAutoRange(false); |
| 452 | + plot.getDomainAxis().setRange(xLower, xUpper); |
| 453 | + plot.getRangeAxis().setRange(globalMin, globalMax); |
| 454 | + |
| 455 | + // --- Legend to the right |
| 456 | + chart.getLegend().setPosition(RectangleEdge.RIGHT); |
| 457 | + chart.getLegend().setBackgroundPaint(Color.WHITE); |
| 458 | +// chart.getLegend().setFrame(BlockBorder.NONE); |
| 459 | + |
| 460 | + // --- Renderer 0: sin band --- |
| 461 | + XYDifferenceRenderer sinBandRenderer = new XYDifferenceRenderer(); |
| 462 | + Color sinBandColor = new Color(sinColor.getRed(), sinColor.getGreen(), sinColor.getBlue(), 40); |
| 463 | + sinBandRenderer.setPositivePaint(sinBandColor); |
| 464 | + sinBandRenderer.setNegativePaint(sinBandColor); |
| 465 | + sinBandRenderer.setSeriesStroke(0, new BasicStroke(0f)); |
| 466 | + sinBandRenderer.setSeriesStroke(1, new BasicStroke(0f)); |
| 467 | +// sinBandRenderer.setOutlinePaint(null); |
| 468 | + sinBandRenderer.setSeriesVisibleInLegend(0, false); // hide sin-max legend entries |
| 469 | + sinBandRenderer.setSeriesVisibleInLegend(1, false); |
| 470 | + |
| 471 | + plot.setDataset(0, sinMinMaxDataset); |
| 472 | + plot.setRenderer(0, sinBandRenderer); |
| 473 | + |
| 474 | + // --- Renderer 1: tan band --- |
| 475 | + XYDifferenceRenderer tanBandRenderer = new XYDifferenceRenderer(); |
| 476 | + Color tanBandColor = new Color(tanColor.getRed(), tanColor.getGreen(), tanColor.getBlue(), 40); |
| 477 | + tanBandRenderer.setPositivePaint(tanBandColor); |
| 478 | + tanBandRenderer.setNegativePaint(tanBandColor); |
| 479 | + tanBandRenderer.setSeriesStroke(0, new BasicStroke(0f)); |
| 480 | + tanBandRenderer.setSeriesStroke(1, new BasicStroke(0f)); |
| 481 | +// tanBandRenderer.setOutlinePaint(null); |
| 482 | + tanBandRenderer.setSeriesVisibleInLegend(0, false); |
| 483 | + tanBandRenderer.setSeriesVisibleInLegend(1, false); |
| 484 | + |
| 485 | + plot.setDataset(1, tanMinMaxDataset); |
| 486 | + plot.setRenderer(1, tanBandRenderer); |
| 487 | + |
| 488 | + // --- Renderer 2: main curves --- |
| 489 | + XYLineAndShapeRenderer lineRenderer = new XYLineAndShapeRenderer(true, false); |
| 490 | + lineRenderer.setSeriesPaint(0, sinColor); |
| 491 | + lineRenderer.setSeriesPaint(1, tanColor); |
| 492 | + lineRenderer.setSeriesStroke(0, new BasicStroke(2f)); |
| 493 | + lineRenderer.setSeriesStroke(1, new BasicStroke(2f)); |
| 494 | + lineRenderer.setSeriesVisibleInLegend(0, true); // keep the main curves visible in Legend |
| 495 | + lineRenderer.setSeriesVisibleInLegend(1, true); |
| 496 | + |
| 497 | + plot.setDataset(2, mainDataset); |
| 498 | + plot.setRenderer(2, lineRenderer); |
| 499 | + |
| 500 | + // --- Renderer 3: STD diamonds --- |
| 501 | + XYLineAndShapeRenderer stdRenderer = new XYLineAndShapeRenderer(false, true); |
| 502 | + Shape diamond = createDiamondShape(4); |
| 503 | + |
| 504 | + stdRenderer.setSeriesShape(0, diamond); |
| 505 | + stdRenderer.setSeriesShape(1, diamond); |
| 506 | + stdRenderer.setSeriesPaint(0, sinColor.darker()); |
| 507 | + stdRenderer.setSeriesPaint(1, tanColor.darker()); |
| 508 | + stdRenderer.setSeriesVisibleInLegend(0, false); |
| 509 | + stdRenderer.setSeriesVisibleInLegend(1, false); |
| 510 | + |
| 511 | + plot.setDataset(3, stdDataset); |
| 512 | + plot.setRenderer(3, stdRenderer); |
| 513 | + |
| 514 | + // --- ChartPanel --- |
| 515 | + ChartPanel panel = new ChartPanel(chart); |
| 516 | + panel.setOpaque(true); // must be opaque for white to show |
| 517 | + panel.setBackground(Color.WHITE); |
| 518 | + |
| 519 | + // --- checkboxes to control display |
| 520 | + JPanel controls = new JPanel(); |
| 521 | + JCheckBox cbAvg = new JCheckBox("Averages", true); |
| 522 | + JCheckBox cbMinMax = new JCheckBox("Min-Max", false); |
| 523 | + JCheckBox cbStd = new JCheckBox("STD", false); |
| 524 | + controls.add(cbAvg); |
| 525 | + controls.add(cbMinMax); |
| 526 | + controls.add(cbStd); |
| 527 | + |
| 528 | + // Averages (dataset 2) |
| 529 | + cbAvg.addActionListener(e -> { |
| 530 | + boolean on = cbAvg.isSelected(); |
| 531 | + plot.getRenderer(2).setSeriesVisible(0, on); |
| 532 | + plot.getRenderer(2).setSeriesVisible(1, on); |
| 533 | + lineRenderer.setSeriesVisibleInLegend(0, true); // force legend visible |
| 534 | + lineRenderer.setSeriesVisibleInLegend(1, true); |
| 535 | + }); |
| 536 | + |
| 537 | + // Min-Max (datasets 0 and 1) |
| 538 | + cbMinMax.addActionListener(e -> { |
| 539 | + boolean on = cbMinMax.isSelected(); |
| 540 | + plot.setRenderer(0, on ? sinBandRenderer : null); |
| 541 | + plot.setRenderer(1, on ? tanBandRenderer : null); |
| 542 | + }); |
| 543 | + |
| 544 | + // STD (dataset 3) |
| 545 | + cbStd.addActionListener(e -> { |
| 546 | + boolean on = cbStd.isSelected(); |
| 547 | + stdRenderer.setSeriesVisible(0, on); |
| 548 | + stdRenderer.setSeriesVisible(1, on); |
| 549 | + }); |
| 550 | + |
| 551 | + // --- Frame --- |
| 552 | + JFrame frame = new JFrame("JFreeChart Min/Max/STD Demo"); |
| 553 | + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
| 554 | + frame.add(panel, BorderLayout.CENTER); |
| 555 | + frame.add(controls, BorderLayout.SOUTH); |
| 556 | + frame.setSize(900, 600); |
| 557 | + frame.setLocationRelativeTo(null); |
| 558 | + frame.setVisible(true); |
| 559 | + }); |
| 560 | + } |
| 561 | + |
| 562 | + private static Shape createDiamondShape(int size) { |
| 563 | + Path2D.Double p = new Path2D.Double(); |
| 564 | + p.moveTo(0, -size); |
| 565 | + p.lineTo(size, 0); |
| 566 | + p.lineTo(0, size); |
| 567 | + p.lineTo(-size, 0); |
| 568 | + p.closePath(); |
| 569 | + return p; |
| 570 | + } |
| 571 | + |
322 | 572 | } |
0 commit comments