怎么在JPanel中实现用Jfreechar实现鼠标放上去就显示多条曲线交点上的数据
时间: 2024-03-09 17:48:32 浏览: 80
JFreechart 双坐标Y轴 折点显示数据
要在JPanel中使用JFreeChart实现鼠标放上去就显示多条曲线交点上的数据,需要对每条曲线都添加一个ChartMouseListener,并在mouseMoved()方法中计算出所有曲线的交点数据。
以下是示例代码:
```java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYItemRendererState;
import org.jfree.data.xy.XYPointerAnnotation;
import org.jfree.ui.Layer;
public class MyChartPanel extends ChartPanel implements ChartMouseListener, MouseMotionListener {
private List<Intersection> intersections = new ArrayList<>();
private boolean showAnnotations = true;
public MyChartPanel(JFreeChart chart) {
super(chart);
for (int i = 0; i < chart.getXYPlot().getDatasetCount(); i++) {
chart.getXYPlot().getRenderer(i).addChangeListener(this);
chart.getXYPlot().getRenderer(i).addChartMouseListener(this);
}
addMouseMotionListener(this);
}
@Override
public void chartMouseClicked(ChartMouseEvent event) {
// do nothing
}
@Override
public void chartMouseMoved(ChartMouseEvent event) {
intersections.clear();
JFreeChart chart = event.getChart();
XYPlot plot = chart.getXYPlot();
double x = plot.getDomainAxis().java2DToValue(event.getTrigger().getX(), getScreenDataArea(), plot.getDomainAxisEdge());
double y = plot.getRangeAxis().java2DToValue(event.getTrigger().getY(), getScreenDataArea(), plot.getRangeAxisEdge());
for (int i = 0; i < plot.getDatasetCount(); i++) {
XYDataset dataset = plot.getDataset(i);
XYItemRenderer renderer = plot.getRenderer(i);
ValueAxis domainAxis = plot.getDomainAxisForDataset(i);
ValueAxis rangeAxis = plot.getRangeAxisForDataset(i);
int seriesCount = dataset.getSeriesCount();
for (int j = 0; j < seriesCount; j++) {
int itemCount = dataset.getItemCount(j);
for (int k = 0; k < itemCount - 1; k++) {
double x1 = dataset.getXValue(j, k);
double y1 = dataset.getYValue(j, k);
double x2 = dataset.getXValue(j, k + 1);
double y2 = dataset.getYValue(j, k + 1);
Intersection intersection = getIntersection(x, y, x1, y1, x2, y2, domainAxis, rangeAxis);
if (intersection != null) {
intersection.setSeriesIndex(j);
intersection.setItemIndex(k);
intersection.setRenderer(renderer);
intersections.add(intersection);
}
}
}
}
repaint();
}
private Intersection getIntersection(double x, double y, double x1, double y1, double x2, double y2, ValueAxis domainAxis, ValueAxis rangeAxis) {
double dx = x2 - x1;
double dy = y2 - y1;
double dr2 = dx * dx + dy * dy;
double D = x1 * y2 - x2 * y1;
double discriminant = 5 * 5 * dr2 - D * D;
if (discriminant < 0) {
return null;
}
double sqrtDiscriminant = Math.sqrt(discriminant);
double x3 = (D * dy + Math.signum(dy) * dx * sqrtDiscriminant) / dr2;
double y3 = (-D * dx + Math.abs(dy) * sqrtDiscriminant) / dr2;
double x4 = (D * dy - Math.signum(dy) * dx * sqrtDiscriminant) / dr2;
double y4 = (-D * dx - Math.abs(dy) * sqrtDiscriminant) / dr2;
if (Double.isNaN(x3) || Double.isNaN(y3) || Double.isNaN(x4) || Double.isNaN(y4)) {
return null;
}
double xMin = Math.min(x1, x2);
double xMax = Math.max(x1, x2);
double yMin = Math.min(y1, y2);
double yMax = Math.max(y1, y2);
if (x3 < xMin || x3 > xMax || y3 < yMin || y3 > yMax || x4 < xMin || x4 > xMax || y4 < yMin || y4 > yMax) {
return null;
}
double xValue = domainAxis.java2DToValue(x3, getScreenDataArea(), domainAxis.getEdge());
double yValue = rangeAxis.java2DToValue(y3, getScreenDataArea(), rangeAxis.getEdge());
return new Intersection(xValue, yValue, x3, y3, x4, y4);
}
public boolean isShowAnnotations() {
return showAnnotations;
}
public void setShowAnnotations(boolean showAnnotations) {
this.showAnnotations = showAnnotations;
repaint();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (showAnnotations) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Intersection intersection : intersections) {
double x = intersection.getX();
double y = intersection.getY();
double x3 = intersection.getX3();
double y3 = intersection.getY3();
double x4 = intersection.getX4();
double y4 = intersection.getY4();
double dx = x4 - x3;
double dy = y4 - y3;
double angle = Math.atan2(dy, dx);
double textX = x3 + 5 * Math.cos(angle);
double textY = y3 + 5 * Math.sin(angle);
XYPointerAnnotation annotation = new XYPointerAnnotation(String.format("(%.2f, %.2f)", x, y), x, y, angle);
annotation.setBaseRadius(0);
annotation.setTipRadius(5);
annotation.setPaint(Color.black);
intersection.setAnnotation(annotation);
intersection.getRenderer().addAnnotation(annotation, Layer.FOREGROUND);
g2.draw(new Line2D.Double(x3, y3, x4, y4));
}
}
}
@Override
public void mouseDragged(MouseEvent e) {
// do nothing
}
@Override
public void mouseMoved(MouseEvent e) {
// do nothing
}
private static class Intersection {
private double x;
private double y;
private double x3;
private double y3;
private double x4;
private double y4;
private int seriesIndex;
private int itemIndex;
private XYItemRenderer renderer;
private XYPointerAnnotation annotation;
public Intersection(double x, double y, double x3, double y3, double x4, double y4) {
this.x = x;
this.y = y;
this.x3 = x3;
this.y3 = y3;
this.x4 = x4;
this.y4 = y4;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getX3() {
return x3;
}
public double getY3() {
return y3;
}
public double getX4() {
return x4;
}
public double getY4() {
return y4;
}
public int getSeriesIndex() {
return seriesIndex;
}
public void setSeriesIndex(int seriesIndex) {
this.seriesIndex = seriesIndex;
}
public int getItemIndex() {
return itemIndex;
}
public void setItemIndex(int itemIndex) {
this.itemIndex = itemIndex;
}
public XYItemRenderer getRenderer() {
return renderer;
}
public void setRenderer(XYItemRenderer renderer) {
this.renderer = renderer;
}
public XYPointerAnnotation getAnnotation() {
return annotation;
}
public void setAnnotation(XYPointerAnnotation annotation) {
this.annotation = annotation;
}
}
}
```
在使用ChartPanel时,可以使用MyChartPanel代替原来的ChartPanel,并调用isShowAnnotations()和setShowAnnotations()方法来控制是否显示交点上的数据。注意,为了在交点上显示数据,需要在paintComponent()方法中添加XYPointerAnnotation,并将它们添加到XYItemRenderer中。同时,为了避免重复添加Annotation,需要在paintComponent()方法中先清除之前添加的Annotation。
阅读全文