Hugin vs Matplotlib Comparison

This document compares the Hugin visualization library (OCaml) with Matplotlib (Python), highlighting similarities, differences, and providing equivalent code examples.

1. Overview

Hugin is a visualization library for OCaml inspired by Matplotlib's API and functionality. Just as Nx provides NumPy-like functionality for OCaml, Hugin provides Matplotlib-like visualization capabilities for the OCaml ecosystem.

Key characteristics of Hugin vs Matplotlib:

  • Cairo with SDL Backend: Hugin uses Cairo for rendering with SDL as the display backend. It has been designed to support multiple backends, with plans to add more in the future.
  • Integration with Nx: Hugin is designed to work seamlessly with Nx, just as Matplotlib works with NumPy arrays.
  • Functional Style: Hugin follows OCaml's functional paradigm, with a pipeline-oriented API that uses the |> operator for chaining operations, making the code flow naturally in OCaml.
  • Limited Feature Set: While Hugin covers the core visualization needs, it's not yet as feature-rich as Matplotlib, focusing on the fundamentals with more features planned for future development.
  • API Structure: While Matplotlib is object-oriented, Hugin uses a more functional approach with modules and functions that align with OCaml's programming model.

2. Basic Image Display

Image Display with imshow

Hugin:

open Hugin

let () =
  if Array.length Sys.argv < 2 then (
    Printf.printf "Usage: %s <image_path>\n" Sys.executable_name;
    exit 1);

  let image_path = Sys.argv.(1) in
  let img = Nx_io.load_image image_path in
  let fig = imshow ~title:"Image" img in
  show fig

Matplotlib:

import sys
import cv2
import numpy as np
import matplotlib.pyplot as plt

if len(sys.argv) < 2:
    print(f"Usage: {sys.argv[0]} <image_path>")
    sys.exit(1)

image_path = sys.argv[1]
img = cv2.imread(image_path)
# Convert BGR (OpenCV default) to RGB for proper display in matplotlib
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

plt.figure()
plt.imshow(img)
plt.show()

3. 2D Plotting

Basic Line Plots

Hugin:

open Hugin
open Nx

let x = linspace float32 0. (2. *. Float.pi) 100
let y1 = map Float.sin x
let y2 = map Float.cos x

let () =
  let fig = figure ~width:800 ~height:600 () in
  let _ =
    subplot fig
    |> Plotting.plot ~x ~y:y1 ~color:Artist.Color.blue ~label:"sin(x)"
    |> Plotting.plot ~x ~y:y2 ~color:Artist.Color.red ~linestyle:Dashed
         ~label:"cos(x)"
    |> Axes.set_title "Trigonometric Functions"
    |> Axes.set_xlabel "Angle (radians)"
    |> Axes.set_ylabel "Value"
    |> Axes.set_ylim ~min:(-1.2) ~max:1.2
    |> Axes.grid true
  in
  show fig

Matplotlib:

import numpy as np
import matplotlib.pyplot as plt

# Create data
x = np.linspace(0, 2 * np.pi, 100)
y1 = np.sin(x)
y2 = np.cos(x)

# Create plot
plt.figure(figsize=(8, 6))
plt.plot(x, y1, color='blue', label='sin(x)')
plt.plot(x, y2, color='red', linestyle='--', label='cos(x)')
plt.title('Trigonometric Functions')
plt.xlabel('Angle (radians)')
plt.ylabel('Value')
plt.ylim(-1.2, 1.2)
plt.grid(True)
plt.legend()
plt.show()

4. 3D Plotting

3D Line/Curve Plots

Hugin:

open Nx

let create_helix_data () =
  let t = Nx.linspace float32 0. (4. *. Float.pi) 100 in
  let x = Nx.map (fun t -> Float.cos t) t in
  let y = Nx.map (fun t -> Float.sin t) t in
  let z = Nx.map (fun t -> t /. (4. *. Float.pi)) t in
  (x, y, z)

let () =
  let x, y, z = create_helix_data () in
  let fig =
    Hugin.plot3d ~title:"3D Helix" ~xlabel:"x" ~ylabel:"y" ~zlabel:"z" x y z
  in
  Hugin.show fig

Matplotlib:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def create_helix_data():
    t = np.linspace(0, 4 * np.pi, 100)
    x = np.cos(t)
    y = np.sin(t)
    z = t / (4 * np.pi)
    return x, y, z

# Generate data
x, y, z = create_helix_data()

# Create figure and 3D axes
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')

# Plot the 3D helix
ax.plot(x, y, z, color='b', linewidth=1.0)

# Set labels and title
ax.set_title("3D Helix")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")

# Display the plot
plt.show()

5. Creating Multiple Subplots

Hugin:

let fig = Figure.create ~width:1600 ~height:1200 () in
let ax1 = Figure.add_subplot ~nrows ~ncols ~index:1 fig in

Matplotlib:

fig = plt.figure(figsize=(16, 12))
ax1 = fig.add_subplot(nrows, ncols, 1)

6. Example: Creating and Displaying a Figure

Hugin:

let fig = figure ~width:800 ~height:600 () in
let _ = subplot fig |> (* plotting commands *) in
show fig

Matplotlib:

plt.figure(figsize=(8, 6))
# plotting commands
plt.show()

7. Example: Customizing Plot Appearance

Hugin:

subplot fig
|> Plotting.plot ~x ~y ~color:Artist.Color.blue ~linestyle:Dashed
   ~marker:Artist.Circle ~label:"Data"
|> Axes.set_title "Plot Title"
|> Axes.grid true

Matplotlib:

ax = plt.subplot()
ax.plot(x, y, color='blue', linestyle='--', marker='o', label='Data')
ax.set_title("Plot Title")
ax.grid(True)