Awesome
ocaml-d3
This library provides OCaml bindings for the core selection API of the D3.js library. It enables you to create type-safe, composable widgets using HTML, SVG, and CSS.
Installation
Install the library and its depenencies via OPAM:
opam install d3
Development
To install development versions of the library, pin the package to the root of your local repository:
opam pin add d3 .
You can install the latest changes by commiting them to the local git repository and running:
opam upgrade d3
For building and running the examples during development, you will need to reconfigure the build process:
./configure --enable-examples
make && $BROWSER examples/rectangle.html
And you're good to go—though styling the TodoMVC app will make it easier on the eyes.
Conceptual Prerequisites
In order to effectively use ocaml-d3, you should be familiar with the core concepts of D3.js, including data joins, nested selections, and reusable charts. The following series of blog posts by @mbostock introduce these concepts quite nicely, and independently of ocaml-d3 should be ready by anbody that is interested developing their D3.js skills:
Usage
ocaml-d3 is designed to be a very literal interpretation of the D3.js API. In fact, any D3.js code that uses only the Core Selection API should be fairly straightforward to port over to ocaml-d3. For example, here's a fragment of code from a D3.js Voronoi diagram example translated to use ocaml-d3:
type point = { x : int, y : int }
voronoi : (point list * point list) D3.t =
nest (selectAll "path" <.> bind cells)
[ enter <.> append "path"
; update
|. fun attr "d" (fun _ ps _ -> path ps)
|. fun attr "class" (fun _ _ i -> "q" ++ (string_of_int (i mod 9)) ++ "-9") ]
Operations such as selectAll
, enter
, and attr
have the same behavior as
their D3.js counterparts. The bind
operation is equivalent to the data()
operator from D3.js, though it requires its argument to be a function.
Similarly, attr
also requires a function as its second argument, which takes
the data bound to the element and the element's index in the selection. Another
difference is that ocaml-d3 replaces method chaining with the |.
operator.
For example,
selectAll "path"
|. bind cells
is equlivalent to
d3.selectAll("path")
.data(cells)
Sequencing is another operation that's slightly different in ocaml-d3. In
Javascript, there's a common pattern where you apply a data bound to a
selection, assign it to a variable, and then apply enter()
, update, and
exit()
operations to the variable. In place of this pattern, you use the
nest
operator. Its use is illustrated in the example above. Below is the
equivalent JavaScript code.
var path = d3.selectAll('path')
.data(function(d) { ... });
path.enter()
.append('path');
path
.attr('d', function(d) { ... })
.attr('class', function(d) { ... });
Rendering
Creating a selection such as voronoi
above does not actually draw anything to
the screen. Rather, it defines a computation that the runtime knows how to draw
to the screen. To do this, you use the run
function. Its first argument is a
selector that will be used as the root element to render within. The second
argument is the datum of type 'a
that will be bound to the selection. The
final argument is D3 selection that will be rendered.
(* val run : string -> 'a -> ('a, _) D3.t -> unit *)
let () = D3.run "body" voronoi [{x = 200, y = 200}; {x = 320, y = 100}]
Linking
As these are simply bindings for the D3.js library, it's necessary to load
D3.js into your web page at some point or another. You may choose to link it in
when converting your project to JavaScript via js_of_ocaml. Alternatively,
you can include it in the page using a <script>
tag.
License
BSD3, see LICENSE file for its text.