Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding some reusable annotations #226

Merged
merged 1 commit into from
Oct 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions src/main/java/org/jfree/chart/annotations/XYDataRangeAnnotation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-2021, by Object Refinery Limited and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* ------------------------
* XYDataRangeAnnotation.java
* ------------------------
* (C) Copyright 2003-2021, by Object Refinery Limited.
*
* Original Author: Yuri Blankenstein (for ESI TNO);
*
*/
package org.jfree.chart.annotations;

import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;

import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.Range;

/**
* This annotation can be put on an {@link XYPlot} to ensure a visible data
* range. The range values should be specified w.r.t. to the first (index 0)
* domain or range axis.
*
* @see XYPlot#getDataRange(ValueAxis)
* @see XYPlot#getDomainAxis()
* @see XYPlot#getRangeAxis()
*/
public class XYDataRangeAnnotation extends AbstractXYAnnotation implements XYAnnotationBoundsInfo {
private static final long serialVersionUID = 2058170262687146829L;

private final Range minimumDomainRange;
private final Range minimumRangeRange;

/**
* Creates a new instance.
*
* @param minimumDomainRange the range to ensure on the domain axis
* ({@code null} permitted).
* @param minimumRangeRange the range to ensure on the range axis
* ({@code null} permitted).
*/
public XYDataRangeAnnotation(Range minimumDomainRange, Range minimumRangeRange) {
this.minimumDomainRange = minimumDomainRange;
this.minimumRangeRange = minimumRangeRange;
}

@Override
public boolean getIncludeInDataBounds() {
return true;
}

@Override
public Range getXRange() {
return minimumDomainRange;
}

@Override
public Range getYRange() {
return minimumRangeRange;
}

@Override
public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis,
int rendererIndex, PlotRenderingInfo info) {
// Nothing to do here
}

@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((minimumDomainRange == null) ? 0 : minimumDomainRange.hashCode());
result = prime * result + ((minimumRangeRange == null) ? 0 : minimumRangeRange.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
XYDataRangeAnnotation other = (XYDataRangeAnnotation) obj;
if (minimumDomainRange == null) {
if (other.minimumDomainRange != null)
return false;
} else if (!minimumDomainRange.equals(other.minimumDomainRange))
return false;
if (minimumRangeRange == null) {
if (other.minimumRangeRange != null)
return false;
} else if (!minimumRangeRange.equals(other.minimumRangeRange))
return false;
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
/* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-2021, by Object Refinery Limited and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* ------------------------
* XYInversePointerAnnotation.java
* ------------------------
* (C) Copyright 2003-2021, by Object Refinery Limited.
*
* Original Author: Yuri Blankenstein (for ESI TNO);
*
*/
package org.jfree.chart.annotations;

import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;

import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.geom.GeomUtil;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.text.TextUtils;
import org.jfree.chart.ui.RectangleEdge;
import org.jfree.chart.util.PublicCloneable;

/**
* An arrow and label that can be placed on an {@link XYPlot}. The arrow is
* drawn at a user-definable angle but points towards the label of the
* annotation.
* <p>
* The arrow length (and its offset from the (x, y) location) is controlled by
* the tip radius and the base radius attributes. Imagine two circles around the
* (x, y) coordinate: the inner circle defined by the tip radius, and the outer
* circle defined by the base radius. Now, draw the arrow starting at some point
* on the outer circle (the point is determined by the angle), with the arrow
* tip being drawn at a corresponding point on the inner circle.
*/
public class XYInversePointerAnnotation extends XYPointerAnnotation
implements Cloneable, PublicCloneable, Serializable {

/** For serialization. */
private static final long serialVersionUID = -4031161445009858551L;

/** The default dot radius (in Java2D units). */
public static final double DEFAULT_DOT_RADIUS = 0;

/** The radius of the dot at the start of the arrow. */
private double dotRadius;

/**
* Creates a new label and arrow annotation.
*
* @param label the label ({@code null} permitted).
* @param x the x-coordinate (measured against the chart's domain axis).
* @param y the y-coordinate (measured against the chart's range axis).
* @param angle the angle of the arrow's line (in radians).
*/
public XYInversePointerAnnotation(String label, double x, double y,
double angle) {
super(label, x, y, angle);
this.dotRadius = DEFAULT_DOT_RADIUS;
}

/**
* Returns the radius of the dot at the start of the arrow.
*
* @return the radius of the dot at the start of the arrow
* @see #setDotRadius(double)
*/
public double getDotRadius() {
return dotRadius;
}

/**
* Sets the radius of the dot at the start of the arrow, &#8804; 0 will omit
* the dot.
*
* @param dotRadius the radius of the dot at the start of the arrow
* @see #getDotRadius()
*/
public void setDotRadius(double dotRadius) {
this.dotRadius = dotRadius;
fireAnnotationChanged();
}

/**
* Draws the annotation.
*
* @param g2 the graphics device.
* @param plot the plot.
* @param dataArea the data area.
* @param domainAxis the domain axis.
* @param rangeAxis the range axis.
* @param rendererIndex the renderer index.
* @param info the plot rendering info.
*/
@Override
public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex,
PlotRenderingInfo info) {

PlotOrientation orientation = plot.getOrientation();
RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
plot.getDomainAxisLocation(), orientation);
RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
plot.getRangeAxisLocation(), orientation);
double j2DX = domainAxis.valueToJava2D(getX(), dataArea, domainEdge);
double j2DY = rangeAxis.valueToJava2D(getY(), dataArea, rangeEdge);
if (orientation == PlotOrientation.HORIZONTAL) {
double temp = j2DX;
j2DX = j2DY;
j2DY = temp;
}

double startX = j2DX + Math.cos(getAngle()) * getBaseRadius();
double startY = j2DY + Math.sin(getAngle()) * getBaseRadius();

double endX = j2DX + Math.cos(getAngle()) * getTipRadius();
double endY = j2DY + Math.sin(getAngle()) * getTipRadius();

// Already calculate the label bounds to adjust the start point
double labelX = j2DX
+ Math.cos(getAngle()) * (getBaseRadius() + getLabelOffset());
double labelY = j2DY
+ Math.sin(getAngle()) * (getBaseRadius() + getLabelOffset());
Shape hotspot = TextUtils.calculateRotatedStringBounds(getText(), g2,
(float) labelX, (float) labelY, getTextAnchor(),
getRotationAngle(), getRotationAnchor());

Point2D[] pointOnLabelBounds = GeomUtil.calculateIntersectionPoints(
new Line2D.Double(startX, startY, endX, endY),
GeomUtil.getLines(hotspot, null));
if (pointOnLabelBounds.length > 0) {
// Adjust the start point to the intersection with the label bounds
startX = pointOnLabelBounds[0].getX();
startY = pointOnLabelBounds[0].getY();
}

double arrowBaseX = startX - Math.cos(getAngle()) * getArrowLength();
double arrowBaseY = startY - Math.sin(getAngle()) * getArrowLength();

double arrowLeftX = arrowBaseX
+ Math.cos(getAngle() + Math.PI / 2.0) * getArrowWidth();
double arrowLeftY = arrowBaseY
+ Math.sin(getAngle() + Math.PI / 2.0) * getArrowWidth();

double arrowRightX = arrowBaseX
- Math.cos(getAngle() + Math.PI / 2.0) * getArrowWidth();
double arrowRightY = arrowBaseY
- Math.sin(getAngle() + Math.PI / 2.0) * getArrowWidth();

GeneralPath arrow = new GeneralPath();
arrow.moveTo((float) startX, (float) startY);
arrow.lineTo((float) arrowLeftX, (float) arrowLeftY);
arrow.lineTo((float) arrowRightX, (float) arrowRightY);
arrow.closePath();

g2.setStroke(getArrowStroke());
g2.setPaint(getArrowPaint());
Line2D line = new Line2D.Double(arrowBaseX, arrowBaseY, endX, endY);
g2.draw(line);
g2.fill(arrow);

if (dotRadius > 0) {
Ellipse2D.Double dot = new Ellipse2D.Double(endX - dotRadius,
endY - dotRadius, 2 * dotRadius, 2 * dotRadius);
g2.fill(dot);
}

// draw the label
g2.setFont(getFont());
if (getBackgroundPaint() != null) {
g2.setPaint(getBackgroundPaint());
g2.fill(hotspot);
}

g2.setPaint(getPaint());
TextUtils.drawRotatedString(getText(), g2, (float) labelX,
(float) labelY, getTextAnchor(), getRotationAngle(),
getRotationAnchor());
if (isOutlineVisible()) {
g2.setStroke(getOutlineStroke());
g2.setPaint(getOutlinePaint());
g2.draw(hotspot);
}

String toolTip = getToolTipText();
String url = getURL();
if (toolTip != null || url != null) {
addEntity(info, hotspot, rendererIndex, toolTip, url);
}

}

@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
long temp;
temp = Double.doubleToLongBits(dotRadius);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
XYInversePointerAnnotation other = (XYInversePointerAnnotation) obj;
if (Double.doubleToLongBits(dotRadius) != Double
.doubleToLongBits(other.dotRadius))
return false;
return true;
}

/**
* Returns a clone of the annotation.
*
* @return A clone.
*
* @throws CloneNotSupportedException if the annotation can't be cloned.
*/
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Loading