Summary of the previous episodes: 10 days ago Richard Jones
complained about the difficulties to achieve simple tasks (drawing a function graph on the screen) on
modern computers with modern programming languages; the day after Erik de Castro
Lopo replied with a post in which he used GTK and Cairo (better: the OCaml bindings) to achieve the
result to draw a simple function on the screen. Yesterday Matias Giovannini added some pepper to this
argument using SDL to draw the Newton
fractal.
So, what can be added to all this? With a perfect graphic toy you can draw on a window with simple commands,
of course, but you also want to interact with the objects you drew. So I elaborated Erik example to add some
keyboard and mouse interaction with the graphics on the screen.
Downloading and compiling
First of all, download the source code or,
if you want the latest version, clone my GIT repository:
$ git clone https://www.ex-nunc.org/projects/pdonadeo/cairo_toy.git cairo_toy.git
To compile the program you need:
- OCaml
(I have version 3.10.2, but probably 3.10.0 or 3.10.1 are ok);
- Lablgtk2,
the OCaml binding to GTK2;
- the OCaml binding to libcairo.
All these packages are available in any recent Linux distribution; on Debian/Ubuntu:
$ aptitude install ocaml liblablgtk2-ocaml-dev libcairo-ocaml-dev
To compile instruct this command inside the program directory:
$ ocamlbuild demo_toy.native
The code
The program is very simple and is essentially derived from Erik's code: the core is the functor
Toy_maker.Make which accepts, as input, a module with the following signature
INTERACTOR:
module type INTERACTOR =
sig
type state
val init_state : state
val win_title : string
val init_width : int
val init_height : int
val cmd_line_handler : state -> string array -> state
val keyboard_callback : state -> GdkEvent.Key.t -> state * bool
val pointer_buttons_callback : state -> GdkEvent.Button.t -> state * bool
val pointer_motion_callback : state -> GdkEvent.Motion.t -> state * bool
val pointer_scroll_callback : state -> GdkEvent.Scroll.t -> state * bool
val repaint : state -> Cairo.t -> int -> int -> state * bool
end
In this module the user must provide a type state, which contains the application state,
some initialization values, a command line handler (in case you need) and 4 event handlers for the following
events:
- keyboard;
- mouse motion;
- mouse buttons;
- mouse wheel event (scroll events in GTK).
The user also provides a repaint function, which takes care of repainting the Cairo
context.
As a demo I wrote a simple My_interactor module implementing the following simple
features:
- left click on the gray background creates a new circle;
- left click inside an existing circle moves it around;
- right click inside a circle deletes it;
- the mouse wheel zooms (in and out);
- middle click is used to pan;
Here is the result.
Yes, it's somewhat dull, but it does its job. Have fun!