Closed matoos32 closed 1 year ago
I noticed the cross-hairs begin to cease lining up when the demo app width is made very large, starting around 2050 pixels. The misalignment increases in magnitude as the app width increases. At extremely large width of 3440 pixels the cross-hairs move in different directions on the sub-plots. Reproduced this on Linux and Windows with Java 8 and 11. Seems like a logic problem somewhere, but the code in this change-set is using the same x-coordinate for all sub-plots. Needs investigating.
I did some debugging. Capturing some notes:
I found that XYPlot
's draw()
method will adjust the crosshair x-value in the presence of an achor. See: https://github.com/jfree/jfreechart/blob/v1.5.3/src/main/java/org/jfree/chart/plot/XYPlot.java#L3002
The anchor is set in the ChartPanel
when handling a Java mouse click. See: https://github.com/jfree/jfreechart/blob/v1.5.3/src/main/java/org/jfree/chart/ChartPanel.java#L1923
This records the click location for use by JFreeChart components downstream, like the XYPlot
draw()
method.
jfreechart-builder
always uses a CombinedDomainXYPlot with subplots even if there's just one plot. This is to maintain code for any number of plots with only one set of underlying logic.
This pull request's solution was to emulate a click on all subplots by calling handleClick()
on them. This would leverage the existing JFreeChart logic to update and display crosshairs, to not write new crosshair logic in jfreechart-builder
. However CombinedDomainXYPlot
provides the anchor only to the draw()
call of the subplot that is clicked on. For the others null
is provided.
The outcome is the clicked-on subplot gets a crosshair x-value adjustment as linked at the top, and the other subplots don't.
The event's 2D coordinate and coordinate-converted value work visibly fine to me / match the anchor-adjustment value when the window size is much narrower than about 2050 pixels, but has a very noticable mismatch with the anchor-adjustment beyond that ~2050. I don't know if it has to do with density of data points along the x-axis, or simply the width of the window.
When the problem manifests, if you examine the x-value returned by the XYPlot
draw()
anchor-adjustment xx = xAxis.java2DToValue(anchor.getX(), dataArea, xAxisEdge);
, where xAxis
is the domain axis, you see its value differs from the x-value obtained using from the ChartMouseEvent
's getTrigger().getX() passed into the x-axis's java2DToValue() in handleClick: https://github.com/jfree/jfreechart/blob/v1.5.3/src/main/java/org/jfree/chart/plot/XYPlot.java#L3970
Looking at java2DToValue()
implementations, both DateAxis
and NumberAxis
used in jfreechart-builder
perform a floating point conversion (and type cast in DateAxis
's case) using a calculated percent of the Java 2D range. See:
DateAxis
: https://github.com/jfree/jfreechart/blob/v1.5.3/src/main/java/org/jfree/chart/axis/DateAxis.java#L743NumberAxis
: https://github.com/jfree/jfreechart/blob/v1.5.3/src/main/java/org/jfree/chart/axis/NumberAxis.java#L523Could these calculations be noticeably sensitive to the magnitude and floating point precision of its parameters?
Some runtime values inspected with the debugger at the window width where a difference starts being noticed (above 1924 pixels). This is for the "Stock Charts Daily | With Time Gaps | All Defaults" demo app case. The variables below are from the code locations linked in this comment. Compare the calculated hvalue
of handleClick()
to the xx
of draw()
:
---------------------------------------
handleClick()
x=528, y=20
hvalue: 1.665302317283E12
draw()
Anchor: Point2D.Double[528.0, 203.0]
dataArea: java.awt.Rectangle[x=8,y=20,width=858,height=359]
xx: 1.665302317283E12
---------------------------------------
handleClick()
x=480, y=20
hvalue: 1.661062198591E12
draw()
Anchor: Point2D.Double[480.0, 336.0]
dataArea: java.awt.Rectangle[x=8,y=20,width=1924,height=359]
xx: 1.661062198591E12
---------------------------------------
handleClick()
x=888, y=20
hvalue: 1.663543577185E12
dataArea: java.awt.geom.Rectangle2D$Double[x=8.0,y=20.0,w=1928.0,h=619.421875] (combined plot)
draw()
Anchor: Point2D.Double[879.0, 274.0]
dataArea: java.awt.Rectangle[x=8,y=20,width=1928,height=359]
xx: 1.663488708928E12
It seems the anchor x-adjustment puts the vertical crosshair line (x-value) at the correct spot. It's the other plots that get a wrong x-crosshair. A solution could perhaps be to somehow pass a custom anchor in draw()
for the other plots.
I also looked at an alternative suggested in 2014 by David Gilbert (JfreeChart author):
https://stackoverflow.com/questions/24188142/jfreechart-dynamic-point-selection-in-chartpanel-using-chartmouselistener-and-m/24200665#24200665. The idea is to use a CrosshairOverlay
with x and y Crosshair
instances. In prototyping I think this may unfortunately only work for non combined plot solutions as the y-value of the crosshair is in the data space, and what value do you use with multiple sub-plots having different data-sets?
In the runtime values in the previous comment there is a 2D x-coordinate difference where the problem occurs: handleClick(): x=888 draw(): anchor x=879
In the cases with no problems these x-coordinates are the same (safe for the int/double type difference)
I found a way to obtain and use the calculated ChartPanel
anchor. I improved the solution with this and added a second commit.
All discovered issues were resolved.
Tested on Linux with Java 8 and 11. Tested on Windows with Java 11.
No new issues observed.
Will merge this now.
ChartCombinedAxisClickDispatcher
that listens forChartPanel
mouse clicks.