Quick start


Before using any function in the library (and assuming the OcamlCanvas.V1 module has been opened), the user should call Backend.init so that the library makes any internal initialization it needs for the current backend.

Creating canvases

Once the backend is initialized, one can create Canvas objects using the Canvas.createOnscreen and Canvas.createOffscreen functions. The first one creates canvases contained in regular windows (which are simulated in the Javascript backend), while the second one creates canvases that are not rendered on screen, which can be useful to save complex images that can then simply be copied to a visible canvas. Onscreen canvases are hidden by default, and Canvas.show should be called on them to make them visible.

Drawing on canvases

Drawing on a canvas can be perfomed using various drawing primitives, the most ubiquitous being Canvas.clearPath, Canvas.moveTo, Canvas.lineTo, Canvas.arc, Canvas.bezierCurveTo, Canvas.fill and Canvas.stroke. These functions allow to build a path step by step and either fill it completely or draw its outline. It is also possible to directly render some text with the Canvas.fillText and Canvas.strokeText functions.

The canvas drawing style can be customized using functions such as Canvas.setFillColor, Canvas.setStrokeColor or Canvas.setLineWidth. The font used to draw text can be specified with the Canvas.setFont function. It is also possible to apply various transformations to a canvas, such as translation, rotation and scaling, with the functions Canvas.transform, Canvas.translate, Canvas.scale, Canvas.rotate and Canvas.shear. All these styling elements can be saved and restored to/from a state stack using the functions Canvas.save and Canvas.restore.

Handling canvas events

Once the canvases are ready, we may start handling events for these canvases. To do so, we use the Backend.run function, which runs an event loop. This function MUST be the last instruction of the program. It takes a single argument, which is a function to be executed when the event loop has finished running. The event loop may be stopped by calling Backend.stop from any update function.

Each event reports at least the canvas on which it occured, and its timestamp. It may also report mouse coordinates for mouse events, or keyboard status for keyboard events.

An actual example

The following program creates a windowed canvas with an orange background, a cyan border, and the “Hello world !” text drawn rotated in the middle. The user may press the “Escape” key or close the window to exit the program. It will show the number of frames displayed when quitting.

open OcamlCanvas.V1

let () =

  Backend.init ();

  let c = Canvas.createOnscreen ~title:"Hello world"
            ~pos:(300, 200) ~size:(300, 200) () in

  Canvas.setFillColor c Color.orange;
  Canvas.fillRect c ~pos:(0.0, 0.0) ~size:(300.0, 200.0);

  Canvas.setStrokeColor c Color.cyan;
  Canvas.setLineWidth c 10.0;
  Canvas.clearPath c;
  Canvas.moveTo c (5.0, 5.0);
  Canvas.lineTo c (295.0, 5.0);
  Canvas.lineTo c (295.0, 195.0);
  Canvas.lineTo c (5.0, 195.0);
  Canvas.closePath c;
  Canvas.stroke c;

  Canvas.setFont c "Liberation Sans" ~size:36.0
    ~slant:Font.Roman ~weight:Font.bold;

  Canvas.setFillColor c (Color.of_rgb 0 64 255);
  Canvas.setLineWidth c 1.0;
  Canvas.save c;
  Canvas.translate c (150.0, 100.0);
  Canvas.rotate c (-. Const.pi_8);
  Canvas.fillText c "Hello world !" (-130.0, 20.0);
  Canvas.restore c;

  Canvas.show c;

  let e1 =
    React.E.map (fun { Event.canvas = _; timestamp = _; data = () } ->
        Backend.stop ()
      ) Event.close

  let e2 =
    React.E.map (fun { Event.canvas = _; timestamp = _;
                       data = { Event.key; char = _; flags = _ }; _ } ->
        if key = KeyEscape then
          Backend.stop ()
      ) Event.key_down

  let e3 =
    React.E.map (fun { Event.canvas = _; timestamp = _;
                       data = { Event.position = (x, y); button } } ->
        Canvas.setFillColor c Color.red;
        Canvas.clearPath c;
        Canvas.arc c ~center:(float_of_int x, float_of_int y)
          ~radius:5.0 ~theta1:0.0 ~theta2:(2.0 *. Const.pi) ~ccw:false;
        Canvas.fill c ~nonzero:false
      ) Event.button_down

  let frames = ref 0L in

  let e4 =
    React.E.map (fun { Event.canvas = _; timestamp = _ } ->
        frames := Int64.add !frames Int64.one
      ) Event.frame

  Backend.run (fun () ->
      ignore e1; ignore e2; ignore e3; ignore e4;
      Printf.printf "Displayed %Ld frames. Goodbye !\n" !frames)