# The Vg tutorial

Vg is designed to be opened in your module. This defines only types and modules in your scope. Thus to use Vg start with :

``````open Gg
open Vg``````

`Gg` gives us types for points (`Gg.p2`), vectors (`Gg.v2`), 2D extents (`Gg.size2`), rectangles (`Gg.box2`) and colors (`Gg.color`).

Later you may want to read `Gg`'s documentation basics but for now it is sufficient to know that each of these types has a literal constructor named `v` in a module named after the capitalized type name – `P2.v`, `V2.v`, etc.

## A collage model

Usual vector graphics libraries follow a painter model in which paths are filled, stroked and blended on top of each other to produce a final image. Vg departs from that, it has a collage model in which paths define 2D areas in infinite images that are cut to define new infinite images to be blended on top of each other.

The collage model maps very well to a declarative imaging model. It is also very clear from a specification point of view, both mathematically and metaphorically. This cannot be said from the painter model where the semantics of an operation like stroking a self-intersecting translucent path —  which usually applies the paint only once —  doesn't directly map to the underlying paint stroke metaphor. The collage model is also more economical from a conceptual point view since image cuts and blends naturally unify the distinct concepts of clipping paths, path strokes, path fills and compositing groups (unsupported for now in Vg) of the painter model.

The collage model introduced in the following sections was stolen and adapted from the following works.

## Infinite images

Images are immutable and abstract value of type `image`. Conceptually, images are seen as functions mapping points of the infinite 2D plane to colors:

`type Vg.image ` ≈  `Gg.p2 -> Gg.color`

The simplest image is a constant image: an image that associates the same color to every point in the plane. For a constant gray of intensity 0.5 this would be expressed by the function:

``fun _ -> Color.gray 0.5``

In Vg the combinator `Vg.I.const` produces constant infinite images, the above function is written:

``let gray = I.const (Color.gray 0.5)``

The module `Vg.I` contains all the combinators to define and compose infinite images. We will explore some of them later. But for now let's just render that fascinating image.

## Rendering

An infinite image alone cannot be rendered. We need a finite view rectangle and a specification of that view's physical size on the render target. These informations are coupled together with an image to form a `Vg.Vgr.renderable`.

Renderables can be given to a renderer for display via the function `Vg.Vgr.render`. Renderers are created with `Vg.Vgr.create` and need a render target value that defines the concrete renderer implementation used (PDF, SVG, HTML canvas etc.).

The following function outputs the unit square of `gray` on a 30x30 millimeters SVG target in the file `/tmp/vg-tutorial.svg`:

``````let svg_of_unit_square i =
try
Out_channel.with_open_bin "/tmp/vg-tutorial.svg" @@ fun oc ->
let size = Size2.v 30. 30. (* mm *) in
let view = Box2.unit in
let r = Vgr.create (Vgr_svg.target ()) (`Channel oc) in
ignore (Vgr.render r (`Image (size, view, i)));
ignore (Vgr.render r `End);
with Sys_error e -> prerr_endline e

let () = svg_of_unit_square gray``````

The result should be an SVG image with a gray square like this:

## Coordinate space

`Vg`'s cartesian coordinate space has its origin at the bottom left with the x-axis pointing right, the y-axis pointing up.

It has no units, you define what they mean to you. However a renderable implicitely defines a physical unit for `Vg`'s coordinate space: the corners of the specified view rectangle are mapped on a rectangular area of the given physical size on the target.

## Scissors and glue

Constant images can be boring. To make things more interesting `Vg` gives you scissors: the `Vg.I.cut` combinator.

This combinator takes a finite area of the plane defined by a path `path` (more on paths later) and a source image `img` to define the image `I.cut path img` that has the color of the source image in the area defined by the path and the invisible transparent black color (`Gg.Color.void`) everywhere else. In other words `I.cut path img` represents this function:

``fun pt -> if inside path pt then img pt else Color.void``

The following code cuts a circle of radius `0.4` centered in the unit square in the `gray` image defined before.

``````let circle = P.empty |> P.circle (P2.v 0.5 0.5) 0.4
let gray_circle = I.cut circle gray``````

Rendered by `svg_of_unit_square` the result is:

Note that the background color surrounding the circle does not belong to the image itself, it is the color of the webpage background against which the image is composited. Your eyes require a wavelength there and `Gg.Color.void` cannot provide it.

`Vg.I.cut` has an optional `area` argument of type `Vg.P.area` that determines how a path should be interpreted as an area of the plane. The default value is ``Anz`, which means that it uses the non-zero winding number rule and for `circle` that defines its interior.

But the `circle` path can also be seen as defining a thin outline area around the ideal mathematical circle of `circle`. This can be specified by using an outline area `O o. The value `o` of type `Vg.P.outline` defines various parameters that define the outline area; for example its width. The following code cuts the `circle` outline area of width `0.04` in an infinite black image.

``````let circle_outline =
let area = `O { P.o with P.width = 0.04 } in
let blue = I.const (Color.v_srgb 0.000 0.439 0.722) in
I.cut ~area circle blue``````

Below is the result and again, except for the blue color, the rest is in fact `Gg.Color.void`.

`Vg.I.cut` gives us scissors but to combine the results of cuts we need some glue: the `Vg.I.blend` combinator. This combinator takes two infinite images `front` and `back` and defines an image ```I.blend front back``` that has the colors of `front` alpha blended on top of those of `back`. `I.blend front back` represents this function:

``let i' = fun pt -> Color.blend (front pt) (back pt)``

If we blend `circle_outline` on top of `gray_circle`:

``let dot = I.blend circle_outline gray_circle``

We get:

The order of arguments in `I.blend` is defined so that images can be blended using the left-associative composition operator `|>`. That is `dot` can also be written as follows:

``let dot = gray_circle |> I.blend circle_outline``

This means that with `|>` and `Vg.I.blend` left to right order in code maps to back to front image blending.

## Transforming images

The combinators `Vg.I.move`, `Vg.I.rot`, `Vg.I.scale`, and `Vg.I.tr` allow to perform arbitrary affine transformations on an image. For example the image `I.move v i` is `i` but translated by the vector `v`, that is the following function:

``fun pt -> img (V2.(pt - v))``

The following example uses `Vg.I.move`. The function `scatter_plot` takes a list of points and returns a scatter plot of the points. First we define a `dot` around the origin, just a black circle of diameter `pt_width`. Second we define the function `mark` that given a point returns an image with `dot` at that point and `blend_mark` that blends a `mark` at a point on an image. Finally we blend all the marks together.

``````let scatter_plot pts pt_width =
let dot =
let circle = P.empty |> P.circle P2.o (0.5 *. pt_width) in
I.const (Color.v_srgb 0.000 0.439 0.722) |> I.cut circle
in
let mark pt = dot |> I.move pt in
let blend_mark acc pt = acc |> I.blend (mark pt) in
List.fold_left blend_mark I.void pts``````

Note that `dot` is defined outside `mark`, this means that all `mark`s share the same `dot`, doing so allows renderers to perform space and time optimizations. For example the SVG renderer will output a single `circle` path shared by all marks.

Here's the result of `scatter_point` on 800 points with coordinates on independent normal distributions.

## Paths

Paths are used to define areas of the plane. A path is an immutable value of type `Vg.path` which is a list of disconnected subpaths. A subpath is a list of directed and connected curved segments.

To build a path you start with the empty path `Vg.P.empty`, give it to `Vg.P.sub` to start a new subpath and give the result to `Vg.P.line`, `Vg.P.qcurve`, `Vg.P.ccurve`, `Vg.P.earc` or `Vg.P.close` to add a new segment and so forth.

Path combinators take the path they act upon as the last argument so that the left-associative operator `|>` can be used to construct paths.

The image below is made by cutting the outline of the single path `p` defined hereafter.

``````let p =
let rel = true in
P.empty |>
P.sub (P2.v 0.1 0.5) |>
P.line (P2.v 0.3 0.5) |>
P.qcurve ~rel (P2.v 0.2 0.5) (P2.v 0.2 0.0) |>
P.ccurve ~rel (P2.v 0.0 (-. 0.5)) (P2.v 0.1 (-. 0.5)) (P2.v 0.3 0.0) |>
P.earc ~rel (Size2.v 0.1 0.2) (P2.v 0.15 0.0) |>
P.sub (P2.v 0.18 0.26) |>
P.qcurve ~rel (P2.v (0.01) (-0.1)) (P2.v 0.1 (-. 0.05)) |>
P.close |>
P.sub (P2.v 0.65 0.8) |>
P.line ~rel (P2.v 0.1 (-. 0.05))
in
let area = `O { P.o with P.width = 0.01 } in
I.const (Color.v_srgb 0.000 0.439 0.722) |> I.cut ~area p``````

Except for `Vg.P.close` which has no other argument but a path, the last point argument before the path argument is always the concrete end point of the segment. When `true` the optional `rel` argument indicates that the coordinates given to the constructor are expressed relative to end point of the last segment (or `P2.o` if there is no such segment).

Note that after a `P.close` or on the `P.empty` path, the call to `P.sub` can be omitted. In that case an implicit `P.sub P2.o` is introduced.

## Remarks and tips

• Angles follow `Gg`'s conventions.
• Matrices given to `Vg.P.tr` and `Vg.I.tr` are supposed to be affine and as such ignore the last row of the matrix.
• Do not rely on the output of printer functions, they are subject to change.
• Rendering results are undefined if path or image data contains NaNs or infinite floats.
• Any string is assumed to be UTF-8 encoded.
• Sharing (sub)image, path and outline values in the definition of an image may result in more efficient rendering in space and time.