package tiny_appkits

  1. Overview
  2. Docs
Application engines from scratch: a spreadsheet, rich text, paint, draw, CAD, editors and more

Install

dune-project
 Dependency

Authors

Maintainers

Sources

0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0

doc/src/tiny_appkits.appkit_paint/Paint.ml.html

Source file Paint.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
(* Claude Code
 *
 * Copyright (C) 2026 Yoann Padioleau
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License
 * (LGPL) as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *)

let dot b p x y = Bitmap.set b x y (Pattern.black p x y)

let line_dots (x0, y0) (x1, y1) =
  let dx = abs (x1 - x0) and dy = -abs (y1 - y0) in
  let sx = if x0 < x1 then 1 else -1 and sy = if y0 < y1 then 1 else -1 in
  (* err is how far the dot is from the true line, times 2 dx dy *)
  let rec go x y err acc =
    let acc = (x, y) :: acc in
    if x = x1 && y = y1 then List.rev acc
    else
      let e2 = 2 * err in
      let x, err = if e2 >= dy then (x + sx, err + dy) else (x, err) in
      let y, err = if e2 <= dx then (y + sy, err + dx) else (y, err) in
      go x y err acc
  in
  go x0 y0 (dx + dy) []

let stroke b ~brush p a z =
  List.iter (fun (x, y) -> List.iter (fun (dx, dy) -> dot b p (x + dx) (y + dy)) brush) (line_dots a z)

let pencil = [ (0, 0) ]

let round r =
  List.concat_map
    (fun dy -> List.filter_map (fun dx -> if (dx * dx) + (dy * dy) <= (r * r) + r then Some (dx, dy) else None)
                 (List.init ((2 * r) + 1) (fun i -> i - r)))
    (List.init ((2 * r) + 1) (fun i -> i - r))

let square n = List.concat_map (fun dy -> List.init n (fun dx -> (dx - (n / 2), dy - (n / 2)))) (List.init n Fun.id)

(* the corners in order: left <= right, top <= bottom *)
let order (x0, y0) (x1, y1) = (min x0 x1, min y0 y1, max x0 x1, max y0 y1)

let fill_rect b p a z =
  let l, t, r, bo = order a z in
  for y = t to bo do
    for x = l to r do
      dot b p x y
    done
  done

let frame_rect b p a z =
  let l, t, r, bo = order a z in
  for x = l to r do
    dot b p x t;
    dot b p x bo
  done;
  for y = t to bo do
    dot b p l y;
    dot b p r y
  done

(* whether dot (x, y)'s centre is inside the oval of the rectangle *)
let in_oval (l, t, r, bo) x y =
  let a = float_of_int (r - l + 1) /. 2. and b = float_of_int (bo - t + 1) /. 2. in
  let cx = float_of_int l +. a and cy = float_of_int t +. b in
  let u = (float_of_int x +. 0.5 -. cx) /. a and v = (float_of_int y +. 0.5 -. cy) /. b in
  x >= l && x <= r && y >= t && y <= bo && (u *. u) +. (v *. v) <= 1.

let oval_dots edge_only b p a z =
  let box = order a z in
  let l, t, r, bo = box in
  for y = t to bo do
    for x = l to r do
      let inside = in_oval box in
      if inside x y then
        let edge = not (inside (x - 1) y && inside (x + 1) y && inside x (y - 1) && inside x (y + 1)) in
        if edge || not edge_only then dot b p x y
    done
  done

let frame_oval = oval_dots true
let fill_oval = oval_dots false