timbod7 / haskell-chart

A 2D charting library for haskell
428 stars 85 forks source link

Axis title gap #65

Open acowley opened 9 years ago

acowley commented 9 years ago

I've been using Chart with the Diagrams backend (itself using the Rasterific backend) and been very impressed. One thing I haven't been able to figure out how to do is increase the gap between axis labels and axis title. In particular, my X-axis title is pressed right up against the axis labels. Modifying axis_label_gap moves both the labels and title. It's entirely possible I've overlooked a way to increase that spacing, but, if not, perhaps an axis_title_gap field could be added to AxisStyle.

timbod7 commented 9 years ago

I have not used that backend combination.

I suspect that this comes down to some sort of mismatch between the metrics being used for the layout, and the actual font being drawn.

Can you provide a screenshot, or, ideally a code example to reproduce the problems.

acowley commented 9 years ago

Example code:

testPlot :: IO ()
testPlot = do env <- defaultEnv bitmapAlignmentFns 640 480
              let chart2diagram = fst . runBackendR env . toRenderable . execEC
                  xs = zip [0..5] [1..6] :: [(Int,Int)]
                  d = chart2diagram $ do
                        layout_title .= "Test Plot"
                        layout_x_axis . laxis_title .= "I Need More Space To Breathe"
                        layout_y_axis . laxis_title .= "Dependent Variable"
                        plot (points "" xs)
              renderRasterific "TestPlot.png" (sizeSpec2D d) 100 d

Results in testplot

timbod7 commented 9 years ago

Can you show the code for renderRasterific? I'd like to try and reproduce this for my own testing.

acowley commented 9 years ago

It's from diagrams-rasterific

Here are more imports for the test program

import Diagrams.Backend.Rasterific
import Diagrams.TwoD.Size (sizeSpec2D)
import Graphics.Rendering.Chart.Backend.Diagrams (defaultEnv, runBackendR)
import Graphics.Rendering.Chart.Easy
timbod7 commented 9 years ago

I have written a short test script to examine font metrics under the various backends:

import Diagrams.Backend.Rasterific
import Diagrams.TwoD.Size (sizeSpec2D)
import qualified Graphics.Rendering.Chart.Backend.Diagrams as BD
import qualified Graphics.Rendering.Chart.Backend.Cairo as BC
import Graphics.Rendering.Chart.Easy

textExample :: ChartBackend ()
textExample = do
  let s = "Some Sample Text"
  withFontStyle (def{_font_size=20}) $ do
    ts <- textSize s
    let p0 = Point 50 50
        vd = Vector 0 (textSizeDescent ts)
        vs = Vector (textSizeWidth ts) (- (textSizeHeight ts))
        p1 = p0 `pvadd` vd
        p2 = p1 `pvadd` vs
        box = rectPath (Rect p1 p2)
    drawText p0 s
    strokePath box

textCairo = BC.cBackendToFile def textExample "text-cairo.png"
textDiagramsSVG = BD.cBackendToFile def textExample "text-diagrams.svg"
textDiagramsRasterific = do
  env <- BD.defaultEnv bitmapAlignmentFns 800 600
  let d = fst $ BD.runBackend env textExample
  renderRasterific "text-rasterific.png" (sizeSpec2D d) 100 d

main = do
  textCairo
  textDiagramsSVG
  textDiagramsRasterific

It just draws a text string with a box around it, with 3 backends.

Cairo: text-cairo

Rasterific:

text-rasterific

It confirmed my suspicions that there is a problem with text metrics when using diagrams/rasterific (and to a lesser extent diagrams/svg). On your charts, the problem is that the text is being rendered not exactly in the correct spot. It's not just the axes titles - the main title is being rendered too high also.

The diagrams library has somewhat primitive support for text rendering. In particular it has no support for text metrics, making accurate placement of the text (or adjacent objects) difficult. The chart library attempts to work around this by obtaining text metrics from the SVGFonts library. This works ok when rendering through diagrams to SVG, but clearly not so well when rendering to other diagrams backends like rasterific.

A good solution would be to write a chart rasterific backend, bypassing diagrams. This would be a moderate chunk of work - something I'm unlikely to find time for in the near futures. I'd be happy to advise anyone who wanted to have a go at this.

A clunkier solution would be to add some extra parameters to the chart diagrams backend, to allow the setting of some custom scale and offset adjustments.

acowley commented 9 years ago

Thanks for digging in to it. Unfortunately, I'm not going to be able to write a new backend soon, either.

Who should we pull in to see if an easier fix can be found? If you think the chart-diagrams backend is doing the right thing, then perhaps we can raise an issue with diagrams-rasterific.

Personally, I'd rather have a hacky solution than nothing so that at least things stay legible. There's not a lot of fine control over labels in Chart yet, so one can view that as either justification for a hacky fix to make the defaults look good enough or as a first step towards providing flexible position control over labels :)

bergey commented 9 years ago

I can take a look at this from the Diagrams side, maybe next week. Maybe diagrams-rasterific and chart-diagrams are using the font metrics from SVGFonts differently.

@jeffreyrosenbluth is working to make Diagrams better at this sort of thing, letting the size of non-text elements depend on the computed size of text. I'm not sure when that will be released, though.

@acowley what versions of diagrams-lib & diagrams-rasterific are you using?

acowley commented 9 years ago

Thanks @bergey! I've been running with

diagrams-lib-1.2.0.7
diagrams-rasterific-0.1.0.5
bergey commented 9 years ago

I think this is indeed a bug in diagrams-rasterific, as I reported above.

acowley commented 9 years ago

@bergey FontyFruity was updated to expose more bounding box information, but when I looked into this, I found that Chart-diagrams uses an SVG path for figuring out text metrics. Doesn't this suggest that the positioning problem is independent of the way diagrams-rasterific handles text?