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

diagram upside down when rescaling when window resized #7

Open
s5k6 opened this issue Jun 3, 2016 · 3 comments
Open

diagram upside down when rescaling when window resized #7

s5k6 opened this issue Jun 3, 2016 · 3 comments

Comments

@s5k6
Copy link

s5k6 commented Jun 3, 2016

Sorry, I guess this example is pretty minimal, but it works..

If you replace the line marked FIRST with the line marked SECOND, then
the diagram flips upside down, and I have no idea why.

Any help would be helpful...
Stefan


> import Control.Monad.Trans ( liftIO )
> import Graphics.UI.Gtk
> import Diagrams.Backend.Cairo
> import Diagrams.Backend.Gtk
> import Diagrams.Prelude

> import           Diagrams.Prelude                hiding (height, width)
> import           Diagrams.Backend.Cairo.Internal
> import           Graphics.UI.Gtk
> import qualified Graphics.Rendering.Cairo        as CG



I was wondering why `defaultRender` requires a `DrawingArea` as
argument [1], but only gets a window from it to draw on [2].  In
contrast, `renderToGtk` is happy with a something of type
`DrawableClass dc => dc` [1].

Why is that?

Why would I want it differently?  I want to have a window that
rescales the diagram contents when it gets resized.  I'm a GUI-noob,
so I took GTK integration from [3]:

> main :: IO ()
> main
>   = do initGUI
>        canvas <- drawingAreaNew
>        canvas `on` sizeRequest $ return (Requisition 200 200)
>        canvas `on` exposeEvent $ renderFigure myFigure   -- below
>        window <- windowNew
>        Graphics.UI.Gtk.set window [ containerChild := canvas]
>        onDestroy window mainQuit
>        widgetShowAll window
>        mainGUI

> renderFigure :: Diagram B -> EventM EExpose Bool
> renderFigure dia = do
>    win <- eventWindow

>    liftIO . renderToGtk win . toGtkCoords $ scale 15 dia     -- FIRST

Above line does not rescale the diagram when the window is resized, so
I wanted to have something like the following:

  >    liftIO . defaultRender' win . toGtkCoords $ dia   -- SECOND

>    return True


Having a look at the sources [2], I decided that I could build such a
`defaultRender'` on my own, basically simplifying `defaultRender` from
[2]:

> defaultRender' :: (Monoid' m, DrawableClass dc)
>                => dc -> QDiagram Cairo V2 Double m -> IO ()
> defaultRender' drawable diagram
>   = renderDoubleBuffered drawable opts diagram
>   where
>     opts w h
>       = CairoOptions
>         { _cairoFileName     = ""
>         , _cairoSizeSpec     = dims (V2 (fromIntegral w) (fromIntegral h))
>         , _cairoOutputType   = RenderOnly
>         , _cairoBypassAdjust = False
>         }

Now if you exchange the two lines marked FIRST and SECOND above, then
this diagram actually scales nicely.  But why on earth is it upside
down?  In case you do not see this: On my machine it is mirrored on
the horizontal line!




To make the example work, I also copied the following 4 functions
from [2]...

> renderDoubleBuffered ::
>   (Monoid' m, DrawableClass dc) =>
>   dc -- ^ drawable to render onto
>   -> (Int -> Int -> Options Cairo V2 Double) -- ^ options, depending on drawable width and height
>   -> QDiagram Cairo V2 Double m -- ^ Diagram
>   -> IO ()
> renderDoubleBuffered drawable renderOpts diagram = do
>   (w,h) <- drawableGetSize drawable
>   let opts = renderOpts w h
>       renderAction = delete w h >> snd (renderDia Cairo opts diagram)
>   renderWithDrawable drawable (doubleBuffer renderAction)
> 
> delete :: Int -> Int -> CG.Render ()
> delete w h = do
>   CG.setSourceRGB 1 1 1
>   CG.rectangle 0 0 (fromIntegral w) (fromIntegral h)
>   CG.fill

> doubleBuffer :: CG.Render () -> CG.Render ()
> doubleBuffer renderAction = do
>   CG.pushGroup
>   renderAction
>   CG.popGroupToSource
>   CG.paint

...and took an example from diagram's tutorial [4].

> node :: Int -> Diagram B
> node n = text (show n) # fontSizeL 1 # fc white <> circle 1 # fc green

> myFigure :: Diagram B
> myFigure = atPoints (trailVertices $ regPoly 6 5) $ map node [1..]


____________________
[1] https://s3.amazonaws.com/haddock.stackage.org/lts-6.1/diagrams-gtk-1.3.0.1/Diagrams-Backend-Gtk.html
[2] https://s3.amazonaws.com/haddock.stackage.org/lts-6.1/diagrams-gtk-1.3.0.1/src/Diagrams-Backend-Gtk.html#defaultRender
[3] http://stackoverflow.com/questions/11885373/how-do-i-use-the-diagrams-library-with-gtk-drawables
[4] http://projects.haskell.org/diagrams/doc/quickstart.html
@byorgey
Copy link
Member

byorgey commented Jun 3, 2016

Cairo/gtk and diagrams have different ideas about the y-axis: in diagrams, the positive y-axis points up (just like in math); in cairo/gtk, it points down (so (0,0) is at the top left of the screen). When a diagram is "adjusted" it is rescaled and centered but also flipped upside down to account for this.

I think the problem is that your code is flipping the diagram twice: toGtkCoords calls adjustDia with the last component of the options record (_cairoBypassAdjust) set to False: this option controls whether the adjustment step is skipped. So False means it is not skipped, and toGtkCoords does stuff like center the diagram and flip it upside down. But then your defaultRender' function also has that option set to False and so the adjustment happens again: the diagram is already properly scaled and centered so nothing happens there, but it does get flipped again. Notice how in the original code, liftIO . renderToGtk win . toGtkCoords, if you look at the implementation of renderToGtk it has _cairoBypassAdjust set to True, so the adjustment step is bypassed, avoiding a second flip.

@s5k6
Copy link
Author

s5k6 commented Jun 4, 2016

Thanks for that illuminating answer.

So the original defaultRender would do it wrong? I cannot test
because I do not have a DrawingArea at hand, and don't know where to
get one from.

I've tried _cairoBypassAdjust = True, but then the diagram does not
resize automatically when the window is resized. So what's the
correct way to do this? Do I have to reflect the diagram myself, as
follows?

...
> defaultRender' drawable diagram
>   = renderDoubleBuffered drawable opts $ scaleY (-1) diagram
>   where
...
>         , _cairoBypassAdjust = False

And about the root of my problem: I have a DrawWindow from
eventWindow and need to run defaultRender on it. How do I get the
required DrawingArea?

@byorgey
Copy link
Member

byorgey commented Jun 17, 2016

Ah, I see now, toGtkCoords does not resize the diagram, it only centers and flips it. But if you have _cairoBypassAdjust = False in defaultRender' then it will also center and flip the diagram, so I think the solution is to just omit the call to toGtkCoords --- you don't need it at all.

Unfortunately I do not understand the GTK backend well enough to answer your questions about DrawingArea.

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

No branches or pull requests

2 participants