[GSoC 2013] Progress - Porting Charts to Diagrams

98 views
Skip to first unread message

Jan Bracker

unread,
Jun 10, 2013, 7:04:03 AM6/10/13
to diagrams...@googlegroups.com, ch...@projects.haskell.org, Tim Docker
Hi everybody,

this is a general progress report of my work so far. Tim was so nice to move some stuff around so all cairo specific code is in one module. You can find this work in the branch "abstract-drawing":

I pulled that work into my fork:

... and from there I made the following changes:

- I renamed all types to remove the "Cairo" prefix.
- I added custom Chart types for all reused cairo types.
- I also created a Backend.Cairo module and moved the cairo specific stuff there (while doing so I ran into some cyclic dependencies and recreated the Types module).

Based on that I started to design a interface for backends. You can see my try on this in the comments of this file:

I think the backend API reflects all functions that are used by Charts. It should also be easy to implement it with cairo as a backend:

data CairoBackend 
  = CairoBackendPDF
  | CairoBackendPNG
  ...

instance ChartBackend CairoBackend where
  type ChartRenderM CairoBackend = Cairo.Render
  type ChartOutput CairoBackend = Renderable a -> Int -> Int -> OutputType -> FilePath -> IO ()

For this to work the CRender monad has to be generalized so it can take an inner backend-specific monad as first type parameter. While writing this I looked at how Diagrams backend mechanism works. I would like to hear some thoughts on what I did (and in general on my changes so far). I don't really like that there are so many functions in the backend class, but I can't see how to make it smaller without making radical changes to the way that Charts works right now (which Tim and I decided against).

Jan

Tim Docker

unread,
Jun 10, 2013, 8:30:58 AM6/10/13
to Jan Bracker, diagrams...@googlegroups.com, ch...@projects.haskell.org
Hi,

On 10/06/13 21:04, Jan Bracker wrote:
> instance ChartBackend CairoBackend where
> type ChartRenderM CairoBackend = Cairo.Render
> type ChartOutput CairoBackend = Renderable a -> Int -> Int ->
> OutputType -> FilePath -> IO ()

I'm not sure we need "Renderable a" here? Renderable is a higher level
chart library concept that combines a rendering function with a minimum
bounding box. I don't think the drawing backend should need to know of this?
> For this to work the CRender monad has to be generalized so it can
> take an inner backend-specific monad as first type parameter.

I am unclear on how this should be structured at this stage... Cairo has
a monadic interface, based upon IO. diagrams can be created purely (I
think), however it's not clear if this will still be the case once font
handling is taken into account.

Rather than have the CRender monad take an extra parameter, I guess one
could just have a separate data type for each backend (ie CRenderCairo
and CRenderDiagrams) and have the drawing code abstracted over a
typeclass implemented by both. Not sure if this would be better...

> While writing this I looked at how Diagrams backend mechanism works. I
> would like to hear some thoughts on what I did (and in general on my
> changes so far). I don't really like that there are so many functions
> in the backend class, but I can't see how to make it smaller without
> making radical changes to the way that Charts works right now (which
> Tim and I decided against).

It should actually be possible to trim this down without too much
effort. There are 8 functions associated with drawing text. It should be
possible to trim these down to 2 functions (one to get the metrics, and
one to actually draw it), and then the others could be implemented in
terms of these.

Tim

Jan Bracker

unread,
Jun 10, 2013, 8:42:06 AM6/10/13
to Tim Docker, diagrams...@googlegroups.com, ch...@projects.haskell.org
Hi,

On 10/06/13 21:04, Jan Bracker wrote:
instance ChartBackend CairoBackend where
  type ChartRenderM CairoBackend = Cairo.Render
  type ChartOutput CairoBackend = Renderable a -> Int -> Int -> OutputType -> FilePath -> IO ()

I'm not sure we need "Renderable a" here? Renderable is a higher level chart library concept that combines a rendering function with a minimum bounding box. I don't think the drawing backend should need to know of this?

I tried to implement a few things based on my approach and you are absolutly right it is not necessary.

For this to work the CRender monad has to be generalized so it can take an inner backend-specific monad as first type parameter.

I am unclear on how this should be structured at this stage... Cairo has a monadic interface, based upon IO. diagrams can be created purely (I think), however it's not clear if this will still be the case once font handling is taken into account.

Rather than have the CRender monad take an extra parameter, I guess one could just have a separate data type for each backend (ie CRenderCairo and CRenderDiagrams) and have the drawing code abstracted over a typeclass implemented by both. Not sure if this would be better...

Still types like "CRenderCairo" and "CRenderDiagrams" have to be Monads, because otherwise we would have to change the rendering logic for every chart to be non-monadic. I will play around with your idea.
 
While writing this I looked at how Diagrams backend mechanism works. I would like to hear some thoughts on what I did (and in general on my changes so far). I don't really like that there are so many functions in the backend class, but I can't see how to make it smaller without making radical changes to the way that Charts works right now (which Tim and I decided against).

It should actually be possible to trim this down without too much effort. There are 8 functions associated with drawing text. It should be possible to trim these down to 2 functions (one to get the metrics, and one to actually draw it), and then the others could be implemented in terms of these.

True. 

Jan Bracker

unread,
Jun 10, 2013, 11:24:34 AM6/10/13
to diagrams...@googlegroups.com, ch...@projects.haskell.org, Tim Docker
Hi,

Andy: The goal is to abstract Charts from Cairo so we can plug in another backend (target is Diagrams). And by that we would also be able to use Sunroof through diagrams. Of course in the end it should also be possible to implement a Sunroof backend on its own.

Tim:

> Rather than have the CRender monad take an extra parameter, I guess one could just have a separate data type for each backend (ie CRenderCairo and CRenderDiagrams) and have the drawing code abstracted over a typeclass implemented by both. Not sure if this would be better...


It seems to be working quite well. So I started generalizing everything outside of the cairo module to use a general ChartBackend m instead of a CRender to draw everything. There are a few spots that are giving me a hard time:

Renderable was implemented in terms of CRender. So I added another type parameter and that worked fine until I hit Legend. There I also added a type parameter. But that blew up the ToRenderable instance. So I had to insert a associated type to relate types to each other. After doing so I was not able to fix whatever went wrong in Plot.Pie. The 'ToRenderable PieLayout' instance gives me this error:

Graphics/Rendering/Chart/Plot/Pie.hs:121:62:
    Could not deduce (RenderableT m a0 ~ PieChart)
    from the context (ChartBackend m)
      bound by the type signature for
                 toRenderable :: ChartBackend m =>
                                 RenderableT m PieLayout -> Renderable m ()
      at Graphics/Rendering/Chart/Plot/Pie.hs:(117,5)-(125,29)
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    In the return type of a call of `pie_plot_'
    In the second argument of `($)', namely `pie_plot_ p'
    In the second argument of `addMargins', namely
      `(toRenderable $ pie_plot_ p)'

I think I know what is going wrong, but I have no idea how to specify the instance it should use there. Maybe I am just to tired to see the solution.

Right now I am trying to make things work without extra type parameters, though the definitions that are based on CRender are giving me a hard time, because then I end up with rank N types.

Brent Yorgey

unread,
Jun 11, 2013, 9:33:35 AM6/11/13
to Jan Bracker, diagrams...@googlegroups.com, ch...@projects.haskell.org, Tim Docker
Hi Jan and all,

Looks like some great progress! I'm excited. =) Just a couple
thoughts: one, let me know if you want help staring at the funny type
errors with Legend and whatnot. I'm not familiar with the Chart code
but perhaps I can help figure out a good solution for the abstraction.

My other general comment is that I know you and Tim have talked a bit
about the problems with having font stuff in IO whereas diagrams is
pure. However, from my point of view it wouldn't be that big of a
deal if Chart produced an IO Diagram rather than just a Diagram.
Maybe I haven't thought about it sufficiently but it seems like this
would be an easy solution to the problem of needing IO. I'm happy to
talk about it in more detail if you want, and if there are ways that
the diagrams API could change to make any of this easier I'm happy to
discuss that as well. I'm in the middle of some big refactoring anyway
(though don't worry, I don't think it will affect your work on porting
Chart very much).

-Brent
> --
> You received this message because you are subscribed to the Google Groups "diagrams-discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to diagrams-discu...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

Jan Bracker

unread,
Jun 12, 2013, 8:27:26 AM6/12/13
to Jan Bracker, diagrams...@googlegroups.com, ch...@projects.haskell.org, Tim Docker, Brent Yorgey
Hi everybody,

Brent: Thanks you for the offer and thanks for your great help on those nasty type errors yesterday.

On IO in Diagrams: We looked into the SVGFont rendering stuff and noticed that it has a call to unsafePerformIO (which let us scream in pain and agony :-P ). We though it would be nice to remove this hidden call and actually offer a function that makes it explicit. I am not sure why it is there and assume its purpose is to keep everything pure. Your suggestion to produce a IO Diagram is what we thought of too. We are not sure which changes would be required up to now, but we will come back to you when we do know.

On general note: I have worked on abstracting the backend the last two days. Here what I accomplished by now:

- I have fully converted my branch to use the abstract rendering backend ChartBackend [0] instead of the CRender monad directly.

- There is a implementation of the backend with Cairo in the Backend.Cairo module [1].

- The current HEAD of the backend-experiment branch [2] is stable (commit ad715c4d216e8fcb494f9919d4fb5459ce7d85c2).

- I added reference images [3] of all tests to compare with the results that the original code delivered.

- All tests are running using the new backend code and pixel-diffs [4] of the produced PNGs show that they render exactly the same.

- I ran into the issue that the ToRenderable class was complicated to abstract. My abstraction involved a associated type with two parameters that are in relation to each other [5,6]. After talking with Tim I removed the ToRenderable code completely and instead just exported specific functions for all types directly. This also made the examples easier to adjust, since the new ToRenderable class made type annotations necessary everywhere, which, in my point of view, defeats the purpose of the toRenderable function. It was easier just calling the specific function directly. I think the change of API is acceptable, since the generalised backend lets type parameters popup everywhere anyway.

The next steps: 

- Tim suggested this simplified API instead of the rather extensive [0] version used right now: http://hpaste.org/89758 . I will work on trimming down the current API and providing all complex functions in terms of the new API. 
- The only downside I see is the Matrix type which (I assume) comes from Cairo.Matrix [7]. I would just give a naive implementation that contains 6 double values for the affine transformations and provide a similar custom API.
- I will also try to provide a small EDSL for writing paths to make that easier (I guess some short combinators and a monoid instance will do fine).

Jan



2013/6/11 Brent Yorgey <byo...@seas.upenn.edu>

Jan Bracker

unread,
Jun 12, 2013, 10:45:02 AM6/12/13
to Jan Bracker, diagrams...@googlegroups.com, ch...@projects.haskell.org, Tim Docker, Brent Yorgey
Hi everybody,

a small follow up: While thinking about and implementing the smaller API I noticed that due to the internals of Cairo the source color is used to render everything. When setting a style (font, fill, line) the source color is changed according to that style. This means each of the "with...Style" or "withSourceColor" functions constantly overrides the source color. Is this really desired behaviour? Shouldn't the fill color apply to fills, the line color to strokes and the font color to text rendering? If so the source color ("withSourceColor") is a useless concept. Of course keeping these color seperate will expand the cairo rendering backend, but I think it is useful to avoid confusion to which color is used where.

Example:

1: withLineStyle ls $ withFillStyle fs $ fillPath p >> strokePath p

should be the same as (according to my thoughts):

2: withFillStyle fs $ withLineStyle ls $ fillPath p >> strokePath p

but with the current implementation 1 would render both with the fill color and 2 would render them with the line color.

Jan



2013/6/12 Jan Bracker <jan.b...@googlemail.com>

Brent Yorgey

unread,
Jun 12, 2013, 12:01:13 PM6/12/13
to Jan Bracker, diagrams...@googlegroups.com, ch...@projects.haskell.org, Tim Docker
We keep the line color and fill color separate in diagrams (and
indeed, we have to do a bit of extra work for the cairo backend since
it only has the one source color). We don't have a separate text
color (the fill color is used) but maybe we should...? That would be
easy enough to change (I'm 95% sure) if it would make things simpler
for you.

-Brent

Jan Bracker

unread,
Jun 12, 2013, 12:28:39 PM6/12/13
to Jan Bracker, diagrams...@googlegroups.com, ch...@projects.haskell.org, Tim Docker
Well, I think the necessity of a font color depends on the context. I don't know if it actually makes sense for diagrams, esspecially since you might want stroke and fill your font separatly. But since the Chart data types all contain a color attribute and fonts are always filled but never stroked it makes sense for Charts.


2013/6/12 Brent Yorgey <byo...@seas.upenn.edu>

Brent Yorgey

unread,
Jun 12, 2013, 1:38:07 PM6/12/13
to Jan Bracker, diagrams...@googlegroups.com, ch...@projects.haskell.org, Tim Docker
OK, makes sense. And in any case when compiling Chart -> diagrams I
guess you are going to want to convert text to paths via SVGFonts
anyway, so how diagrams handles text natively is irrelevant for you.

-Brent

Jan Bracker

unread,
Jun 12, 2013, 2:05:50 PM6/12/13
to Jan Bracker, diagrams...@googlegroups.com, ch...@projects.haskell.org, Tim Docker
Yes, that's true. We need to go that way so we can get text metrics.


2013/6/12 Brent Yorgey <byo...@seas.upenn.edu>

Tim Docker

unread,
Jun 12, 2013, 5:54:42 PM6/12/13
to Jan Bracker, diagrams...@googlegroups.com, ch...@projects.haskell.org, Brent Yorgey
Hi,

I think your analysis is good. In the context of the backend, it makes sense to separate out the color from the various styles.

On the other hand, in the context of the chart use API, it makes sense to have all of the properties of a line in a single data type - it's simpler for the caller to say style this plot line using properties as per this value.

Possible solutions are:

    1) Leave it and put up with the inconsistency...
    2) Have separate types in the backend API / user API for LineStyle, FillStyle and FontStyle
    3) Remove withSourceColor from the backend API, and require backends to give the logical behaviour (ie setting the FillStyle doesn't affect the color of lines). This would mean the cairo backend would have to do a bit more work to be correct.

Either 2 or 3 is ok with me.

Tim

Jan Bracker

unread,
Jun 13, 2013, 4:48:37 AM6/13/13
to Tim Docker, diagrams...@googlegroups.com, ch...@projects.haskell.org, Brent Yorgey
I will proceed with 3 then.


2013/6/12 Tim Docker <t...@dockerz.net>

Jan Bracker

unread,
Jun 17, 2013, 10:34:54 AM6/17/13
to Tim Docker, ch...@projects.haskell.org, diagrams...@googlegroups.com, Brent Yorgey
Hello everybody,

I made good progress over the last week. I implemented the minimal chart drawing API (  http://hpaste.org/89758 ) that Tim suggested. It worked with minor problems. I tried to stay pixel equal to the PNGs that the current release produces when running the test suite. I did not manage to stay 100% pixel equal, but all differences are not visible with the naked eye.

Here the changes I had to apply to the API:

- TextSize also contains the Y-bearing since it is used in some of the text adjustment calculations.
- withSourceColor does not exists any more. The line style provides the color for stroking, fill style the color for filling and font style the color for fonts. There are several utilities to aid implementors of backends with keeping the local environment up to date.
- The strokePath and fillPath functions are now backendStrokePath and backendFillPath. They take an extra parameter that tells them if the given path shall be close before rendering. This was necessary to produce equal output. Convenience functions that default to not closed paths are provided.
- The matrix implementation is basically a copy of the matrix implementation from cairo.
- The withClipRegion function now takes a Maybe Rect. I thought this might be useful, because otherwise there would be no possibility to remove the clip region. Though this is not needed anywhere in charts yet.
- I had to add the fillClip function, because otherwise there would have been no possibility to fill the clip region if it is not set (infinite plane clip).
- showText is now drawText and also takes a Point so it know where to draw the text. The point refers to the left end of the text baseline.
- The drawing module now contains all kinds of higher level drawing functions and helpers. There are still a lot of legacy functions with temporary names. I will remove these soon.

This commit is "stable" [0]: bdb628591132964e94f5b317026605790250359a

Plans I would like to hear thoughts to:

- Should I prefix the backend functions and provide wrapper functions with the nice name? I already did this for backendFillPath since I noticed that it also needed to know if the path has to be closed, but in 95% of all cases the version that does not close by default is more practical. Like this we would also be able to change the backend interface without having to change several other modules.
- Should I keep the Maybe in withClipRegion? As said nothing in charts actually ever removes the clip region.

The next steps would be:

- Remove legacy functions. 
- Document all the drawing functions.
- Create a separate package for the Cairo backend. I don't know if we should merge with the main repo before this or afterwards.
- There are still some direct connections to the Cairo backend in Simple.Internal we have to think about how these can be removed.
- I would also suggest to create a separate package for the tests since that would make dependency management easier.

Greets
Jan



2013/6/13 Jan Bracker <jan.b...@googlemail.com>

Jan Bracker

unread,
Jun 25, 2013, 10:01:17 AM6/25/13
to Tim Docker, ch...@projects.haskell.org, diagrams...@googlegroups.com, Brent Yorgey
Hello everybody,

the last week brought some major changes. The current stable version is here [0]. All changes are in the master branch of my fork. What has been done / changed:

- Implemented a new type for 'Path' that supports to be closed. Like this 'fillPath' and 'strokePath' do not need a boolean flag anymore. This should also make future changes easier, since the 'Path' type is abstract.
- Cleaned up the code and added documentation for the 'Backend', 'Geometry' and 'Drawing' functions.
- 'withClipRegion' now works in a hierarchical manner. You can only make the clipping region smaller and you can only clip on areas in your current clip. The clip region is represented by a 'Limit Rect'. This properly reflects the possibilities that the clip region can be empty 'LMin' or an infinite plane 'LMax'.
- Removed the associated 'ChartOutput' type from the 'ChartBackend' class. Instead backend modules now deliver functions specific to their backend.
- Removed all function with legacy names and switched the complete library to use the new API and functions.
- Marked all 'default...' values as deprecated to push the transition to Data.Default.
- Removed the 'Types' module again and distributed all contents into 'Backend', 'Geometry' and 'Drawing'.
- Moved the cairo backend and the tests to their own packages ( chart-cairo and chart-tests ).
- Updated/Fixed the 'chart-gtk' package to work together with all changes. Its tests are now also in the chart-tests package.

Some questions that I have:

- When implementing the new version of 'withClipRegion' I stumbled upon the question how the empty clip region looks in cairo. Right now I use the empty 'Rect' at location (0,0) to represent this. This seems to work fine.
- Right now the 'Renderable' class is commented out. Should I try to reintroduce it? Is this really a good Idea or should we leave it away all together? None of Charts uses it anymore.

My plan for the next week would be:

- Implement a test that only uses the backend API so we have something more basic to check if the backends work as expected. This will also show if the current documentation is specific enough.
- Try to bring 'Renderable' back into scope (If that is a good idea)

The next big step after this would be to merge all the changes back into the main Charts repository and start developing the Diagrams backend.

Greets


2013/6/17 Jan Bracker <jan.b...@googlemail.com>

Jan Bracker

unread,
Jul 2, 2013, 5:17:57 AM7/2/13
to Tim Docker, Charts Mailing List, diagrams...@googlegroups.com, Brent Yorgey
Hello everybody,

here my current progress over the last few days:

- 'ToRenderable' is now completely removed.
- Extended the documentation with information about font handling, the coordinate system and the units used in backends.
- Wrote several functions to test backends. I am planning to save the PNGs produced by the cairo backend as reference images and link them in the documentation, so people know how it's supposed to look.
- Fixed some bugs I noticed while writing the tests.

It seems like cabal does not support the separate compilation of executable. So we have to split the test package to manage dependencies. I would suggest to move all test code that is backend independent into the charts library (a module: Backend.Test). All backend specific tests can be put into the specific backend (also a module). Like that people can use the tests without having so download the package separatly and start doing custom stuff in the package directory. This would also force the test to keep in sync with the rest of the library, because cabal will always compile them along with the rest.

The most current stable version of the port can be found here [0] in commit 2e2277b4a65dc426d2da2a92b4c27eaa6bc11ecd .

Greets
Jan



2013/6/25 Jan Bracker <jan.b...@googlemail.com>

Brent Yorgey

unread,
Jul 2, 2013, 7:56:53 AM7/2/13
to Jan Bracker, Tim Docker, Charts Mailing List, diagrams...@googlegroups.com
On Tue, Jul 02, 2013 at 11:17:57AM +0200, Jan Bracker wrote:
>
> It seems like cabal does not support the separate compilation of
> executable. So we have to split the test package to manage dependencies.

That's not true. (Assuming I understand what you are saying.) You
can use some (standard) tricks with cabal flags to make this work.
For example, see the diagrams-builder package, which has several
executables (each with different dependencies) but you control which
are installed via flags, and you only incur the dependencies of the
ones you request to be installed. The idea is to make a flag like

flag cairo
description: install cairo-specific builder tool
default: False
manual: True

("default" means how the flag is set if the user does not explicitly
specify; "manual" means that the cabal s

and then in the
executable section you make both 'buildable' and the dependencies
conditional on the flag, like this:

executable diagrams-builder-cairo
main-is: diagrams-builder-cairo.hs
hs-source-dirs: src/tools
default-language: Haskell2010
other-extensions: DeriveDataTypeable
RecordWildCards

if !flag(cairo)
buildable: False

if flag(cairo)
build-depends: base >= 4 && < 5,
filepath,
directory,
diagrams-builder,
diagrams-lib >= 0.6 && < 0.7,
diagrams-cairo >= 0.6 && < 0.7,
cmdargs >= 0.6 && < 0.11

When building, if you want to turn on a specific flag you do something like

cabal install -fcairo diagrams-builder

-Brent

Brent Yorgey

unread,
Jul 2, 2013, 9:04:33 AM7/2/13
to Jan Bracker, Tim Docker, Charts Mailing List, diagrams...@googlegroups.com
Whoops, looks like my email got munged a bit. The parenthetical in
the middle was supposed to say

("default" means how the flag is set if the user does not explicitly
specify; "manual" means that the cabal solver should not
automatically try toggling the flag when creating a build plan,
i.e. the flag should be changed only by the user.)

-Brent

Jan Bracker

unread,
Jul 10, 2013, 6:12:32 AM7/10/13
to Jan Bracker, Tim Docker, Charts Mailing List, diagrams...@googlegroups.com
Hello everybody,

thanks for your input on the flags Brent! I no used them to steer separate compilation in the test package.

What changed during the last week:
- I fixed the 'PickFn' returns I accidentally removed. They now work as before and I restored them everywhere I removed them.
- I removed a lot of unused language extensions.
- The packages are now named according to the scheme set by the Chart and Chart-gtk package.
- Utility functions for implementing backends are now in the Backend.Utils module.
- As suggested I removed the splitbase flag from all packages.
- Each executable in the Chart-test package has to be activated by a flag now.
- I fixed some bugs. Most important a bug that transformations are applied in the wrong order.

The latest "stable" version can be found here [0].

Aside from this work I completed an alternate implementation of the backend interface, based on a deep embedding instead of a type class. This worked perfectly and removes the necessity of the additional type parameter in many types. This alternate implementation can be seen in the 'deep-backend' branch of my fork [1]. I also noticed that this alternate implementation can give implementors a better infrastructure to implement backends. This can be seen in the Backend.Utils module [2].

Greets
Jan



2013/7/2 Brent Yorgey <byo...@seas.upenn.edu>

Jan Bracker

unread,
Jul 16, 2013, 5:42:41 AM7/16/13
to Jan Bracker, Tim Docker, Charts Mailing List, diagrams...@googlegroups.com
Hello everybody,

Tim and I decided to go along with the deep embedding, instead of the type class based approach. Here the pros and cons of both:

Deep embedding:
+ Does not need additional type parameters on all using types and functions.
~ Implementing a backend requires more wiring to be done, but that wiring can be done by utility functions.

Type class approach:
+ Easy to implement.
- There are some mistakes an implementer can make (mainly because of the environment, which has to be updated correctly)
- Needs additional type parameters in all types and functions involving it.

This change is not merged with the master branch [0]. Here a list of all changes since last week:

- The instructions now all take the complete environment, so all information is available at each point.
- I started implementing the Diagrams backend.
- As I started to implement the Diagrams backend I noticed that I needed additional information (especially the actual changes instead of just the new environment.) This is why I added the Change data type and provide this information now [1].
- I had to make the constructors of path public, due to issues while implementing paths in the diagrams backend. Still searching for a nice way to solve is abstractly.

All packages inside the master branch [0], except the chart-diagrams package, should be "stable". chart-diagrams does not work yet.

There is other things that came up while implementing the Diagrams backend. I already discussed it with Brent, but I need further input on this topic:
"fillClip" is a very imperative operation. And for infinite clip planes there is no way to implement it in diagrams, since it is built hierarchical and you never know how big your diagram really is. For this reason I would suggest to introduce a background color to the environment instead of the "fillClip" operation. The specific rendering backend can then use the background color to fill everything before drawing the actual chart. This should not cause a problem since the "fillClip" operation is only used in exactly one location [2] and there it can easily be replaced by a "fillPath" operation. Another question that arrives here is whether or not one should be aloud to locally modify the background color. I am not sure what this would mean and I don't see a necessity for it either right now.

Greets
Jan



2013/7/10 Jan Bracker <jan.b...@googlemail.com>

Jan Bracker

unread,
Jul 23, 2013, 6:07:19 AM7/23/13
to Jan Bracker, Tim Docker, Charts Mailing List, diagrams...@googlegroups.com
Hello everybody,

here what I did over the last week:

- Along with switching to the deep embedding as backend interface, we also simplified the embedding and removed the environment. Due to the simplification a backend can now provide an implementation itself and itself decide which information it wants to keep. There was also some refactoring that came along with these changes.
- I have now implemented a diagrams backend package, that is able to do all path operations. 
- The rendering of text also works, but there are still issues with text metrics. These come from a bug within the SVGFonts package [0].

The current head is "stable", except for the text metrics [1]. The diagrams backend works with diagrams 0.6, _not_ the current head of diagrams!

I am working on fixing that bug with SVGFonts, but while doing so I ran into some inconveniences. First of all it seems that the current head of SVGFonts is already using the new additions to the handling of paths. So I had to install all the new diagrams stuff first. I will try to send a pull request soon. I will have to adjust the diagrams backend to work with those changes.

Aside of that I also noticed that the text metric information in SVGFonts are not given in device coordinates. Am I right in assuming they are given in 1000em, because the value "units-per-em" says 1000? If that is right I will try to dig my way through the textSVG' function to find out how to convert that into device coordinates.

Greet
Jan



2013/7/16 Jan Bracker <jan.b...@googlemail.com>

Stephen Tetley

unread,
Jul 23, 2013, 12:25:30 PM7/23/13
to Jan Bracker, diagrams...@googlegroups.com
Hi Jan

In the PostScript world, glyph metrics are given as 1/1000 of a point size. One would hope SVG might follow this lead.

Although out-of-date in the TrueType world, the AFM spec from Adobe is a readable (and skimmable) introduction to glyph metrics and still a worthwhile reference.

Jan Bracker

unread,
Jul 29, 2013, 11:39:52 AM7/29/13
to Stephen Tetley, diagrams...@googlegroups.com
Hello everybody,

here the latest progress on the project:

- While using SVGFonts I fixed a few minor bugs in it.
- The "ToRenderable" type class is reintroduced, because the type complications are not there anymore.
- Some clean up and more specific documentation (especially paths).
- Switched from data-default to data-default-class.
- Standard alignment functions are not provided instead of redifined in each backend.
- Updated the diagrams backend to use the HEAD of diagrams instead of the hackage version.
- All my work until 25th of July has been merged into the main charts repo!
- Worked on the font support in the diagrams backend. This works well now.
- Included standard fonts in the diagrams backend package. This also includes additional versions to support bold and italic fonts.

The current head is "stable", except for the text metrics [1]. I figured out the metrics. Some issues I still have:

- For some reason SVGFonts seems to be rendering fonts of same size considerably smaller then cairo. Not sure what the reason is. Going to double check on this.
- One of the tests is still missing some lines.
- Quality of font rendering is notably worse with SVGFonts. I do not think this can really be improved except by adding native text rendering to diagrams (+ font metrics). Though, when using sans-serif and monospace fonts quality is acceptable (Adobe seems to be doing good work with Source Sans Pro and Source Code Pro). Maybe I will check for an alternative serif font, though it will be hard to beat the character support of Linux Libertine.

Greetings
Jan



2013/7/23 Stephen Tetley <stephen...@gmail.com>

Jan Bracker

unread,
Jul 31, 2013, 10:10:38 AM7/31/13
to Charts Mailing List, Diagrams Disscussion List, Tim Docker, Brent Yorgey
A short update on my font issues:

I created a test case to see the sizing differences between SVGFonts and cairo [0]. There I finally found out what the issue was. As it seems SVG fonts and the cairo font API use different definitions of terms like ascent and descent. I adjusted the metric calculations in the diagrams backend so it mostly fits the semantics that cairo has [1]. There still is a minor difference in the font size and the descent. I suspect the size difference is due to different rendering techniques (hinting vs. plain paths). I can't see why the descents are different, but I do not see that as a problem.

The quality issue isn't that big anymore either. With the adjusted font size you can actually read what SVGFonts outputs quite well. You will notice the difference, but it is not an important issue anymore. 

But the performance of SVGFonts is notably worse compared to using cairo.



2013/7/29 Jan Bracker <jan.b...@googlemail.com>

Jan Bracker

unread,
Aug 5, 2013, 10:34:37 AM8/5/13
to Diagrams Disscussion List, Charts Mailing List, Brent Yorgey, Tim Docker
Hello everybody,

this weeks changes are not to many. I only fixed the last bug on my list. It was caused by not correctly setting the line, fill and font style to their default value before starting to render in the backend. This is fixed now and both backends work well. There are some minor rendering differences which appeared after merging with the new lens code. I have no idea why they appear, but again they are not visible with the naked eye. You can find the latest code here [0].

The further plan: Speed is an issue when using SVGFonts to render text. That is why we are planning to write a translator that creates Haskell source from an SVG font file. This way things should speed up, because the font does not have to be read during runtime anymore and we also hope the constant expression optimizations of the compiler also lead to speedups. Besides speed this also solves the other problem of hidden IO in SVGFonts, it will allow pure access to the included fonts.
I searched through Hackage to find Haskell source generators and my only hits were template Haskell, haskell-src [1] and haskell-src-exts [2]. I think I will use haskell-src-exts, since it is more up to date then haskell-src and I do not see the necessity for this utility to depend on something as complex as template Haskell.

Greets
Jan



2013/8/1 Tim Docker <t...@dockerz.net>

My week is not turning out very well :-(

I will be a bit late tonight. I'm sure you can continue without me.

Sorry!

On 31 Jul 2013 21:45, "Brent Yorgey" <byo...@seas.upenn.edu> wrote:
Yes, I've been quite impressed with Jan's progress and am excited to
try it out!  I will indeed be available then and would be happy to
discuss text handling. I will have to leave at 12:00UTC but that
should give us plenty of time for discussion.

-Brent

On Wed, Jul 31, 2013 at 07:16:37AM +1000, Tim Docker wrote:
> Hi Brent,
>
> Jan has made great progress, and has charts more or less up and
> running atop diagrams (see below).
>
> Currently the text handling is done outside the diagrams libs, just
> rendering the paths. It would be good if we could discuss with you
> potential approaches for improving the diagrams native text support.
>
> Is there any chance you may be around on IRC at 11:00UTC on thursday?
> If that time doesn't suit, I'm sure we can arrange another soon.
>
> Tim
>
>
> On 30/07/13 22:25, Jan Bracker wrote:
> >Tomorrow 9:30pm sounds good for me.
> >
> >Jan
> >
> >
> >2013/7/30 Tim Docker <t...@dockerz.net <mailto:t...@dockerz.net>>
> >
> >    Hi,
> >
> >    I'm sorry for the late notice, but I won't be able to make our IRC
> >    meeting this tonight.
> >
> >    Either tomorrow at 9:30pm, or Thursday at 9:00pm are fine with me.
> >
> >    Tim
> >>    <mailto:stephen...@gmail.com>>

> >>
> >>        Hi Jan
> >>
> >>        In the PostScript world, glyph metrics are given as 1/1000 of
> >>        a point size. One would hope SVG might follow this lead.
> >>
> >>        Although out-of-date in the TrueType world, the AFM spec from
> >>        Adobe is a readable (and skimmable) introduction to glyph
> >>        metrics and still a worthwhile reference.
> >>
> >>        http://partners.adobe.com/public/developer/en/font/5004.AFM_Spec.pdf
> >>
> >>        Best wishes
> >>
> >>        Stephen
> >>
> >>        On 23 July 2013 11:07, Jan Bracker
> >>        <jan.b...@googlemail.com
> >>        <mailto:jan.b...@googlemail.com>> wrote:
> >>
> >>
> >>
> >>            Aside of that I also noticed that the text metric
> >>            information in SVGFonts are not given in device
> >>            coordinates. Am I right in assuming they are given in
> >>            1000em, because the value "units-per-em" says 1000? If
> >>            that is right I will try to dig my way through the
> >>            textSVG' function to find out how to convert that into
> >>            device coordinates.
> >>
> >>
> >>
> >>
> >>
> >>
> >>    _______________________________________________
> >>    Chart mailing list
> >>    Ch...@projects.haskell.org  <mailto:Ch...@projects.haskell.org>
> >>    http://projects.haskell.org/cgi-bin/mailman/listinfo/chart
> >
> >
>

Jan Bracker

unread,
Aug 12, 2013, 11:19:28 AM8/12/13
to Diagrams Disscussion List, Charts Mailing List, Brent Yorgey, Tim Docker
Hello everybody,

I spent last week working on the SVG font to Haskell code converter. I got a running version here [0]. It's in the "font-converter" directory. While writing and testing it I ran into a few problems:

When I output all data into one single big file it does not compile (actually it does seem to compile but after 25 minutes I gave up on letting it finish). So I now divide the data into several modules. After doing so everything compiled, but each of the modules containing the outlines took about 1 to 2 minutes to compile although each only contained about 30 glyphs. For small fonts this was bearable but unpleasant, when converting Linux Libertine I would have spent 2 hours compiling it. So right now I do not hard code the glyphs paths into code anymore and instead added code to generate the outline from the string encoded paths during run time. I am not sure if this still solves the performance problems we have when rendering. I am going to check on that soon. If this does not help with the performance problem I would try to rewrite the path parser using attoparsec or similar libraries.

Besides this compilation and performance issue, we can now include fonts that do not need IO to load.

Greets
Jan



2013/8/5 Jan Bracker <jan.b...@googlemail.com>

Jan Bracker

unread,
Aug 19, 2013, 9:58:12 AM8/19/13
to Diagrams Disscussion List, Charts Mailing List
Hello everybody,

last week I:

- Fixed some bugs in SVGFonts.
- Added a interface to preload customs fonts in the diagrams backend.
- Fixed a bug with filling multiple sub trails in the diagrams backend.
- I worked on fixing the bug of charts being rendered upside down in SVGs when using diagrams-svg. I got a solution for this but it seems like a hack and I would like to discuss this [0].
- I added nice utilities to render SVG and EPS files directly using the diagrams backend. This pulls in some other dependencies but as it is the major use case you would want them anyway.

I did not really work on the font converter to much. Instead I started profiling the tests to see where the application spends most of its time. According to the profiling information when running the DiagramsCairo.hs [1] test about 45% of the computation time is spent inside the textSVG' / makeString function [2]. When trying to analyse the sub expressions I found out that 90% of that time is spent inside the call to scaleY. I am not sure where to go from here, because it seems like most of the computation is due to diagrams arithmetic. Only 10% of the time is spent with actual rendering and 15% with extracting the characters from the string.

Greets
Jan



2013/8/12 Jan Bracker <jan.b...@googlemail.com>

Brent Yorgey

unread,
Aug 19, 2013, 8:18:00 PM8/19/13
to Jan Bracker, Diagrams Disscussion List, Charts Mailing List
On Mon, Aug 19, 2013 at 03:58:12PM +0200, Jan Bracker wrote:
> Hello everybody,
>
> last week I:
>
> - Fixed some bugs in SVGFonts.
> - Added a interface to preload customs fonts in the diagrams backend.
> - Fixed a bug with filling multiple sub trails in the diagrams backend.
> - I worked on fixing the bug of charts being rendered upside down in SVGs
> when using diagrams-svg. I got a solution for this but it seems like a hack
> and I would like to discuss this [0].
> - I added nice utilities to render SVG and EPS files directly using the
> diagrams backend. This pulls in some other dependencies but as it is the
> major use case you would want them anyway.
>
> I did not really work on the font converter to much. Instead I started
> profiling the tests to see where the application spends most of its time.
> According to the profiling information when running the DiagramsCairo.hs
> [1] test about 45% of the computation time is spent inside the textSVG' /
> makeString function [2]. When trying to analyse the sub expressions I found
> out that 90% of that time is spent inside the call to scaleY. I am not sure
> where to go from here, because it seems like most of the computation is due
> to diagrams arithmetic. Only 10% of the time is spent with actual rendering
> and 15% with extracting the characters from the string.

Very interesting. I profiled another large example during Hac Phi and
found that a lot of time was being spent in rotateBy. So it seems
that perhaps Transformations are somehow implicated in performance
issues. The way we represent Transformations certainly carries some
overhead. I am not sure how to go about optimizing things. Any
suggestions or help are most welcome.

-Brent

Jan Bracker

unread,
Sep 2, 2013, 11:35:46 AM9/2/13
to Diagrams Disscussion List, Charts Mailing List
Hello everybody,

I worked on embedding fonts into the output SVGs of Diagrams to reduce the time it takes to render charts and the size of output SVGs. This was a success! Using the standard SVG output without embedded fonts all tests run in about 50 seconds, while using the embedded fonts they run in about 38 seconds on my machine. So we are talking about as speedup of ~20%. The output SVGs also got smaller in size. Most lost ~50% of their size, depending on how much text they actually use. None of the tests is bigger then a megabyte anymore.

The sources for this change can be found in the font-embedding branch of chart-diagrams [0]. I also had to change diagrams-svg [1] and SVGFonts [2] for this to work (their changes are also in the branch font-embedding). I will soon merge this changes back to master and send out a few pull requests. chart-diagrams now offers four functions to use this optimization: renderableToEmbeddedFontSVGFile and renderableToEmbeddedFontSVG (together with their primed variant) [3].

Here a detailed list of what I have done over the past days:

- Fixed rendering issues with chart-diagrams when rendering SVGs (they were upside down and scaled weird).
- runBackend' now takes a switch to decide whether it should render using the native text or with SVGFonts, it also collects used characters when rendering native.
- Added a WriteFont module to SVGFonts that enables it to write a read font back to SVG.
- Enabled to use the font family name of the default fonts in addition to their generic names.
- Added an approximation of text alignments in diagrams-svg (was not implemented yet).
- Worked on correct support of line miter limits in diagrams.
- Fixed a bug with stacked clippings in diagrams-svg.
- Fixed text rendering issues with the chart-diagrams backend.
- Added a hook in diagrams-svg font to add custom definitions to produces SVG files (so I can write the fonts in the files).
- Added functions that render to SVG and actually embed the fonts in chart-diagrams.

Greets
Jan



2013/8/19 Jan Bracker <jan.b...@googlemail.com>

Jan Bracker

unread,
Sep 9, 2013, 12:06:16 PM9/9/13
to Diagrams Disscussion List, Charts Mailing List
Hello Everybody,

the changes to SVGFonts and diagrams-svg are now merged with their master branches. Some time soon we will release a new version of Charts containing the optimized SVG feature.

I worked on refactoring the Layout1 type of Charts over the past week. Currently this is one structure that combines the ability to render one Y axis or two Y axis in the same plot. Tim suggested to divide it into Layout (for ploting one y axis over one x axis) and LayoutLR (for ploting two different y axis over one x axis). This should make the API a bit cleaner, ensure that the top and bottom axis (which refer to the same data) will always have the same labels and ticks and it also enables two Y axis with different type, which is not possible with the old Layout1. This is work in progress and can be seen in the layout-refactor [0] branch of my fork.

Greets
Jan



2013/9/2 Jan Bracker <jan.b...@googlemail.com>

Jan Bracker

unread,
Sep 16, 2013, 8:33:50 AM9/16/13
to Diagrams Disscussion List, Charts Mailing List
Hello Everybody,

the refactoring of Layout1 was finished over the past week. Here the changes:

- AxisVisibility type was introduced to determine which parts of an axis shall be displayed at all. It contains three flags to turn the axis line, labels and ticks on or off.
- _laxis_visible was removed from LayoutAxis since its function is now replaced by the new AxisVisibility type.
- The compression of x labels was removed from StackedLayouts. Each Layout now controls which axis are displayed itself. Also StackedLayout supports both types of layout.
- All tests were modified to work with the new interface and produce the same charts as before.
- Also updated all documentation on code that I touched.

The work can be seen in the layout-refactor branch [0]. This work together with the font embedding things is in progress of being merged with the rest of chart as of now [1].

As the Google Summer of Code is coming to an end this week I guess this will be my last report.


2013/9/9 Jan Bracker <jan.b...@googlemail.com>
Reply all
Reply to author
Forward
0 new messages