Skip to content

Fix #834: custom tick formatter breaks logarithmic axis#973

Merged
timmolter merged 2 commits into
developfrom
timmolter-fix-834-log-axis-custom-formatter
Jun 19, 2026
Merged

Fix #834: custom tick formatter breaks logarithmic axis#973
timmolter merged 2 commits into
developfrom
timmolter-fix-834-log-axis-custom-formatter

Conversation

@timmolter

Copy link
Copy Markdown
Member

Problem

When a custom axis tick label formatter was set alongside a logarithmic axis (setYAxisLogarithmic(true) + setCustomYAxisTickLabelsFormatter(...)), the logarithmic scaling was silently discarded. The formatter received linearly-spaced values (0, 1e8, 2e8 ...) instead of the expected powers-of-ten (1, 10, 100 ... 1e8), and the chart rendered as a linear axis.

The root cause: in both Axis_Y.getAxisTickCalculatorForY() and Axis_X.getAxisTickCalculatorForX(), the custom-formatter check came first in the if/else chain and unconditionally routed to AxisTickCalculator_Callback, which inherits the base linear calculate() method. The logarithmic branch was never reached.

Approach

The fix combines logarithmic tick-position calculation with custom label formatting:

  • AxisTickCalculator_Logarithmic -- all formatterLogNumber.format() calls inside calculate() are replaced with axisFormat.format(). Since axisFormat defaults to formatterLogNumber, existing behavior is unchanged. Two new constructors accept a Function<Double, String> and set axisFormat = new Formatter_Custom(fn) before calculate() runs, so tick positions remain logarithmic while tick labels come from the callback.

  • Axis_Y / Axis_X -- a new combined-case branch is added above the plain callback branch: when both a custom formatter and logarithmic mode are active, it routes to the new constructors instead of AxisTickCalculator_Callback.

No changes to base classes; the fix is fully contained in these three files.

Testing

  • Two new unit tests in XYChartTest (Y-axis and X-axis variants) verify the chart renders without throwing.
  • TestForIssue834.java demo reproduces the reporter's exact use case -- logarithmic Y-axis with an energy unit formatter (nJ / µJ / mJ / J). The axis now shows correct log spacing with custom labels.

When both a custom tick label formatter and logarithmic mode were active
on the same axis, the formatter check took priority and routed to
AxisTickCalculator_Callback, which uses linear tick calculation. This
caused the axis to render as linear and the formatter to receive
linearly-spaced values (0, 1e8, 2e8 …) instead of logarithmically-
spaced powers of ten (1, 10, 100 … 1e8).

Fix:
- Add new constructors to AxisTickCalculator_Logarithmic that accept a
  Function<Double, String> and set axisFormat = Formatter_Custom before
  calculate() runs, so tick positions remain logarithmic while labels
  are produced by the callback.
- Change calculate() to use axisFormat.format() throughout instead of
  the hardcoded formatterLogNumber.format() calls.
- In Axis_Y and Axis_X, add a combined-case branch that routes to the
  new constructors when both custom formatter and logarithmic mode are
  active (checked before the plain callback branch).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes #834 where enabling a custom axis tick label formatter caused logarithmic axes to silently fall back to linear scaling by ensuring logarithmic tick positions are still computed while allowing custom tick labels.

Changes:

  • Extend AxisTickCalculator_Logarithmic to support custom label formatting while preserving logarithmic tick calculation.
  • Update Axis_Y / Axis_X tick-calculator selection to route the “logarithmic + custom formatter” combination to the logarithmic calculator instead of the callback (linear) calculator.
  • Add regression tests and a standalone demo reproducer for issue #834.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
xchart/src/test/java/org/knowm/xchart/XYChartTest.java Adds regression tests for custom formatter + log axis combinations.
xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisTickCalculator_Logarithmic.java Allows log tick calculator to use a custom formatter for labels while keeping log spacing.
xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis_Y.java Routes Y-axis “custom formatter + log mode” to the log tick calculator.
xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis_X.java Routes X-axis “custom formatter + log mode” to the log tick calculator.
xchart-demo/src/main/java/org/knowm/xchart/standalone/issues/TestForIssue834.java Adds a runnable demo reproducing #834’s scenario.

Comment thread xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis_X.java Outdated
Comment thread xchart/src/test/java/org/knowm/xchart/XYChartTest.java
Comment thread xchart/src/test/java/org/knowm/xchart/XYChartTest.java
- Axis_X: add DataType.Date guard to the log+custom-formatter branch so
  Date x-axes continue to use AxisTickCalculator_Date even when a custom
  formatter is also set (mirrors the existing guard on the plain log branch)
- XYChartTest: strengthen both log-axis tests to assert the formatter
  receives powers-of-ten tick values, not linearly-spaced ones; a silent
  regression back to linear would now fail the assertion
- TestForIssue834: add Javadoc to getChart() marking it headless-safe

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@timmolter

Copy link
Copy Markdown
Member Author

Uploading Screenshot 2026-06-20 at 12.08.09 AM.png…

@timmolter timmolter merged commit 17ab6b2 into develop Jun 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants