Library
Module
Module type
Parameter
Class
Class type
The OCaml-Canvas library
The OCaml-Canvas library provides a portable Canvas framework for OCaml. It features an interface similar to HTML5 Canvas, and can render to native X11, MS Windows or macOS windows. It also features a small wrapper around HTML5 Canvas, allowing to use the library in any web browser. In addition, it allows to handle keyboard and mouse events.
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.
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 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
.
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.
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
in
let e2 =
React.E.map (fun { Event.canvas = _; timestamp = _;
data = { Event.key; char = _; flags = _ }; _ } ->
if key = KeyEscape then
Backend.stop ()
) Event.key_down
in
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
in
let frames = ref 0 in
let e4 =
React.E.map (fun { Event.canvas = _; timestamp = _ } ->
frames := Int64.add !frames Int64.one
) Event.frame
in
Backend.run (fun () ->
ignore e1; ignore e2; ignore e3; ignore e4;
Printf.printf "Displayed %Ld frames. Goodbye !\n" !frames)
module V1 : sig ... end
The OCaml-Canvas module is versioned. This is version 1. It is guaranteed that this interface will always remain compatible with existing programs, provided that the modules defined here ARE NEVER included in other modules nor opened globally. Local opens should be performed very carefully, as new identifiers may be introduced in modules and thus shadow any identifier defined before the open directive. An effort will be made to avoid introducing new identifiers that are of length 3 of less, or starting with a single character followed by an underscore. Hence such identifiers should be safe to use without risking to be shadowed.