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

Add crosshair label padding and adjusted crosshair label position when LabelAnchor is center #414

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
35 changes: 35 additions & 0 deletions src/main/java/org/jfree/chart/plot/Crosshair.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import org.jfree.chart.api.RectangleInsets;
import org.jfree.chart.internal.HashUtils;
import org.jfree.chart.labels.CrosshairLabelGenerator;
import org.jfree.chart.labels.StandardCrosshairLabelGenerator;
Expand Down Expand Up @@ -108,6 +109,11 @@ public class Crosshair implements Cloneable, PublicCloneable, Serializable {
*/
private double labelYOffset;

/**
* The label padding.
*/
private RectangleInsets labelPadding;

/**
* The label font.
*/
Expand Down Expand Up @@ -170,6 +176,7 @@ public Crosshair(double value, Paint paint, Stroke stroke) {
this.labelAnchor = RectangleAnchor.BOTTOM_LEFT;
this.labelXOffset = 5.0;
this.labelYOffset = 5.0;
this.labelPadding = RectangleInsets.ZERO_INSETS;
this.labelFont = new Font("Tahoma", Font.PLAIN, 12);
this.labelPaint = Color.BLACK;
this.labelBackgroundPaint = new Color(0, 0, 255, 63);
Expand Down Expand Up @@ -409,6 +416,30 @@ public void setLabelYOffset(double offset) {
this.pcs.firePropertyChange("labelYOffset", old, offset);
}

/**
* Returns the label padding.
*
* @return The label padding (never {@code null}).
* @see #setLabelPadding
*/
public RectangleInsets getLabelPadding() {
return labelPadding;
}

/**
* Sets the label padding and sends a property change event (with the name
* 'labelPadding') to all registered listeners.
*
* @param padding the padding ({@code null} not permitted).
* @see #getLabelPadding()
*/
public void setLabelPadding(RectangleInsets padding) {
Args.nullNotPermitted(padding, "padding");
RectangleInsets old = this.labelPadding;
this.labelPadding = padding;
this.pcs.firePropertyChange("labelPadding", old, padding);
}

/**
* Returns the label font.
*
Expand Down Expand Up @@ -609,6 +640,9 @@ public boolean equals(Object obj) {
if (this.labelYOffset != that.labelYOffset) {
return false;
}
if (!this.labelPadding.equals(labelPadding)) {
return false;
}
if (!this.labelFont.equals(that.labelFont)) {
return false;
}
Expand Down Expand Up @@ -649,6 +683,7 @@ public int hashCode() {
hash = HashUtils.hashCode(hash, this.labelGenerator);
hash = HashUtils.hashCode(hash, this.labelXOffset);
hash = HashUtils.hashCode(hash, this.labelYOffset);
hash = HashUtils.hashCode(hash, this.labelPadding);
hash = HashUtils.hashCode(hash, this.labelFont);
hash = HashUtils.hashCode(hash, this.labelPaint);
hash = HashUtils.hashCode(hash, this.labelBackgroundPaint);
Expand Down
74 changes: 53 additions & 21 deletions src/main/java/org/jfree/chart/swing/CrosshairOverlay.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import java.util.ArrayList;
import java.util.List;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.api.RectangleInsets;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.Crosshair;
import org.jfree.chart.plot.PlotOrientation;
Expand Down Expand Up @@ -279,20 +280,33 @@ protected void drawHorizontalCrosshair(Graphics2D g2, Rectangle2D dataArea,
Font savedFont = g2.getFont();
g2.setFont(crosshair.getLabelFont());
RectangleAnchor anchor = crosshair.getLabelAnchor();
Point2D pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset());
RectangleInsets padding = crosshair.getLabelPadding();
Point2D pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset(), padding);
float xx = (float) pt.getX();
float yy = (float) pt.getY();
TextAnchor alignPt = textAlignPtForLabelAnchorH(anchor);
Shape hotspot = TextUtils.calculateRotatedStringBounds(
label, g2, xx, yy, alignPt, 0.0, TextAnchor.CENTER);
hotspot = padding.createOutsetRectangle(hotspot.getBounds2D());
if (!dataArea.contains(hotspot.getBounds2D())) {
anchor = flipAnchorV(anchor);
pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset());
pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset(), padding);
xx = (float) pt.getX();
yy = (float) pt.getY();
if (anchor == RectangleAnchor.CENTER || alignPt.isHalfAscent()) {
double labelHeight = hotspot.getBounds2D().getHeight();
double minY = dataArea.getY() + (labelHeight + padding.getTop() - padding.getBottom()) / 2.0;
double maxY = dataArea.getY() + dataArea.getHeight() - (labelHeight + padding.getBottom() - padding.getTop()) / 2.0;
if (yy < minY) {
yy = (float) (minY);
} else if (yy > maxY) {
yy = (float) (maxY);
}
}
alignPt = textAlignPtForLabelAnchorH(anchor);
hotspot = TextUtils.calculateRotatedStringBounds(
label, g2, xx, yy, alignPt, 0.0, TextAnchor.CENTER);
hotspot = padding.createOutsetRectangle(hotspot.getBounds2D());
}

g2.setPaint(crosshair.getLabelBackgroundPaint());
Expand Down Expand Up @@ -338,20 +352,33 @@ protected void drawVerticalCrosshair(Graphics2D g2, Rectangle2D dataArea,
Font savedFont = g2.getFont();
g2.setFont(crosshair.getLabelFont());
RectangleAnchor anchor = crosshair.getLabelAnchor();
Point2D pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset());
RectangleInsets padding = crosshair.getLabelPadding();
Point2D pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset(), padding);
float xx = (float) pt.getX();
float yy = (float) pt.getY();
TextAnchor alignPt = textAlignPtForLabelAnchorV(anchor);
Shape hotspot = TextUtils.calculateRotatedStringBounds(
label, g2, xx, yy, alignPt, 0.0, TextAnchor.CENTER);
hotspot = padding.createOutsetRectangle(hotspot.getBounds2D());
if (!dataArea.contains(hotspot.getBounds2D())) {
anchor = flipAnchorH(anchor);
pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset());
pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset(), padding);
xx = (float) pt.getX();
yy = (float) pt.getY();
if (alignPt.isHorizontalCenter()) {
double labelWidth = hotspot.getBounds2D().getWidth();
double minX = dataArea.getX() + (labelWidth + padding.getLeft() - padding.getRight()) / 2.0;
double maxX = dataArea.getX() + dataArea.getWidth() - (labelWidth + padding.getRight() - padding.getLeft()) / 2.0;
if (xx < minX) {
xx = (float) (minX);
} else if (xx > maxX) {
xx = (float) (maxX);
}
}
alignPt = textAlignPtForLabelAnchorV(anchor);
hotspot = TextUtils.calculateRotatedStringBounds(
label, g2, xx, yy, alignPt, 0.0, TextAnchor.CENTER);
hotspot = padding.createOutsetRectangle(hotspot.getBounds2D());
}
g2.setPaint(crosshair.getLabelBackgroundPaint());
g2.fill(hotspot);
Expand All @@ -377,11 +404,12 @@ protected void drawVerticalCrosshair(Graphics2D g2, Rectangle2D dataArea,
* @param anchor the anchor point.
* @param deltaX the x-offset.
* @param deltaY the y-offset.
* @param padding the label padding
*
* @return The anchor point.
*/
private Point2D calculateLabelPoint(Line2D line, RectangleAnchor anchor,
double deltaX, double deltaY) {
double deltaX, double deltaY, RectangleInsets padding) {
double x, y;
boolean left = (anchor == RectangleAnchor.BOTTOM_LEFT
|| anchor == RectangleAnchor.LEFT
Expand All @@ -402,32 +430,36 @@ private Point2D calculateLabelPoint(Line2D line, RectangleAnchor anchor,
x = line.getX1();
y = (line.getY1() + line.getY2()) / 2.0;
if (left) {
x = x - deltaX;
}
if (right) {
x = x + deltaX;
x = x - deltaX - padding.getRight();
} else if (right) {
x = x + deltaX + padding.getLeft();
} else {
x = x + (padding.getLeft() - padding.getRight()) / 2.0;
}
if (top) {
y = Math.min(line.getY1(), line.getY2()) + deltaY;
}
if (bottom) {
y = Math.max(line.getY1(), line.getY2()) - deltaY;
y = Math.min(line.getY1(), line.getY2()) + deltaY + padding.getTop();
} else if (bottom) {
y = Math.max(line.getY1(), line.getY2()) - deltaY - padding.getBottom();
} else {
y = y + (padding.getTop() - padding.getBottom()) / 2.0;
}
}
else { // horizontal
x = (line.getX1() + line.getX2()) / 2.0;
y = line.getY1();
if (left) {
x = Math.min(line.getX1(), line.getX2()) + deltaX;
}
if (right) {
x = Math.max(line.getX1(), line.getX2()) - deltaX;
x = Math.min(line.getX1(), line.getX2()) + deltaX + padding.getLeft();
} else if (right) {
x = Math.max(line.getX1(), line.getX2()) - deltaX - padding.getRight();
} else {
x = x + (padding.getLeft() - padding.getRight()) / 2.0;
}
if (top) {
y = y - deltaY;
}
if (bottom) {
y = y + deltaY;
y = y - deltaY - padding.getBottom();
} else if (bottom) {
y = y + deltaY + padding.getTop();
} else {
y = y + (padding.getTop() - padding.getBottom()) / 2.0;
}
}
return new Point2D.Double(x, y);
Expand Down