This guide demonstrates how to use the Oktree library for efficient nearest neighbor search in 3D space.
Add to your dune file:
(library (name my_library) (libraries oktree gg))
Oktree is designed to work with the Gg library's V3 module, though you can provide your own VEC3 implementation.
The octree is built using a functor that takes a VEC3 module:
open Gg
(* Instantiate the octree functor with Gg.V3 *)
module Okt = Oktree.Make (V3)
(* Create a list of 3D points *)
let points = [
V3.v 0.0 0.0 0.0;
V3.v 1.0 0.0 0.0;
V3.v 0.0 1.0 0.0;
V3.v 0.0 0.0 1.0;
]
(* Build the tree *)
let tree = Okt.of_list pointsOnce the tree is built, query for nearest matches:
(* Find the closest point to (0.1, 0.1, 0.1) *)
let query = V3.v 0.1 0.1 0.1
let nearest = Okt.nearest tree query
(* nearest = V3.v 0.0 0.0 0.0 *)
(* Find nearest to another point *)
let query2 = V3.v 0.9 0.05 0.05
let nearest2 = Okt.nearest tree query2
(* nearest2 = V3.v 1.0 0.0 0.0 *)The ~leaf_size parameter controls when octree subdivision stops. Smaller values create deeper trees with more nodes but fewer points per leaf. Larger values create shallower trees.
(* Small leaf size: more subdivision, faster queries for sparse data *)
let tree_small = Okt.of_list ~leaf_size:4 points
(* Large leaf size: less subdivision, faster construction *)
let tree_large = Okt.of_list ~leaf_size:32 points
(* Default is 16, which works well for most use cases *)
let tree_default = Okt.of_list pointsTuning guidelines:
Finding the nearest color in a palette:
open Gg
module Okt = Oktree.Make (V3)
(* Define a palette of RGB colors as 3D points *)
let palette = [
V3.v 1.0 0.0 0.0; (* red *)
V3.v 0.0 1.0 0.0; (* green *)
V3.v 0.0 0.0 1.0; (* blue *)
V3.v 1.0 1.0 0.0; (* yellow *)
V3.v 1.0 0.0 1.0; (* magenta *)
V3.v 0.0 1.0 1.0; (* cyan *)
]
let palette_tree = Okt.of_list ~leaf_size:8 palette
(* Quantize an arbitrary color to the nearest palette color *)
let quantize color = Okt.nearest palette_tree color
(* Example: quantize orange to nearest palette color *)
let orange = V3.v 1.0 0.5 0.0
let quantized = quantize orange
(* quantized = V3.v 1.0 0.0 0.0 (red) *)
(* Quantize a purple-ish color *)
let purple = V3.v 0.6 0.2 0.8
let quantized2 = quantize purple
(* quantized2 = V3.v 1.0 0.0 1.0 (magenta) *)When processing many queries, reuse the same tree:
(* Build tree once *)
let tree = Okt.of_list ~leaf_size:16 large_point_set
(* Query many times *)
let process_queries queries =
List.map (fun query ->
let nearest = Okt.nearest tree query in
(query, nearest)
) queries
let results = process_queries many_query_pointsThe tree is immutable, but you can insert new points:
(* Start with initial points *)
let tree = Okt.of_list initial_points
(* Add a new point (returns new tree) *)
let tree2 = Okt.insert tree new_point
(* Add multiple points *)
let tree3 = List.fold_left Okt.insert tree2 more_pointsNote: For bulk updates, it is more efficient to rebuild the tree with of_list than to insert points one by one.
While Gg.V3 is recommended, you can use any type that implements the VEC3 signature:
module My_Vec3 : Oktree.VEC3 with type t = float * float * float = struct
type t = float * float * float
let x (x, _, _) = x
let y (_, y, _) = y
let z (_, _, z) = z
let of_tuple t = t
let to_tuple t = t
let add (x1, y1, z1) (x2, y2, z2) = (x1 +. x2, y1 +. y2, z1 +. z2)
let sub (x1, y1, z1) (x2, y2, z2) = (x1 -. x2, y1 -. y2, z1 -. z2)
let div (x1, y1, z1) (x2, y2, z2) = (x1 /. x2, y1 /. y2, z1 /. z2)
let map f (x, y, z) = (f x, f y, f z)
let norm (x, y, z) =
sqrt (x *. x +. y *. y +. z *. z)
let pp fmt (x, y, z) =
Format.fprintf fmt "(%g, %g, %g)" x y z
end
module Okt = Oktree.Make (My_Vec3)
let tree = Okt.of_list [(0., 0., 0.); (1., 1., 1.)]
let nearest = Okt.nearest tree (0.5, 0.5, 0.5)Extract all points from a tree:
let points = Okt.to_list tree
(* Returns all points in the tree as a list *)
(* Check how many points are in the tree *)
let count = List.length pointsUse the provided printer for debugging:
(* Print tree structure *)
Format.printf "%a@." Okt.pp tree
(* Output shows the tree structure with leaf contents *)Construction:
of_list: O(n log n) where n is the number of pointsinsert: O(log n) but less efficient than bulk constructionQueries:
nearest: O(log n) average case for well-distributed pointsnearest: O(n) worst case for highly clustered pointsSpace:
The implementation is optimized for static or slowly-changing point sets:
Best used when:
Not ideal when:
See the Oktree module for the complete API documentation.