3D Surface Plots
Visualize functions of two variables with mesh and surf
This tutorial teaches you how to create 3D surface visualizations in Equana using mesh() and surf() functions. You'll learn to visualize mathematical functions of two variables, create coordinate grids with meshgrid, and produce interactive 3D plots.
Key Concepts: Surface plots display a function as a 3D surface. The
surf()function creates filled surfaces whilemesh()creates wireframes.
Basic Surface Plot
The simplest way to create a surface is to pass a matrix of Z values directly. The X and Y coordinates are automatically generated as row and column indices.
# Create a simple 3D surface from a matrix
# Each element Z(i,j) represents the height at position (i,j)
Z = [1, 2, 3, 4; 2, 4, 6, 8; 3, 6, 9, 12; 4, 8, 12, 16]
surf(Z)
title("Basic Surface Plot")
xlabel("Column")
ylabel("Row")
zlabel("Value")
println("Matrix Z:")
println(Z)Creating Coordinate Grids with meshgrid
For mathematical surfaces, you typically want to define X and Y coordinates explicitly. The meshgrid() function creates 2D coordinate matrices from 1D vectors — essential for evaluating functions over a rectangular domain.
Given vectors x and y, meshgrid(x, y) returns:
- X: A matrix where each row contains the x values
- Y: A matrix where each column contains the y values
This allows you to compute using element-wise operations.
# meshgrid creates 2D coordinate matrices from vectors
# This is essential for defining surfaces over a domain
x = linspace(-2, 2, 5)
y = linspace(-2, 2, 5)
(X, Y) = meshgrid(x, y)
# Now X and Y are matrices where:
# - X has the same value across each row
# - Y has the same value down each column
println("Vector x:")
println(x)
println("Vector y:")
println(y)
println("X matrix (x values repeated in rows):")
println(X)
println("Y matrix (y values repeated in columns):")
println(Y)Gaussian Surface (Bell Curve)
A 2D Gaussian creates a classic bell curve shape. The function is:
Notice how element-wise operations (.^) work naturally with meshgrid outputs. The X and Y variables from the previous cell persist, but we'll create a finer grid for a smoother surface.
# Create a 2D Gaussian surface (bell curve)
# Using a finer grid for smoother visualization
(X, Y) = meshgrid(linspace(-3, 3, 31), linspace(-3, 3, 31))
# Gaussian function: exp(-(x^2 + y^2))
# Use element-wise power .^ for matrices
Z = exp(-(X.^2 + Y.^2))
surf(X, Y, Z)
title("2D Gaussian (Bell Curve)")
xlabel("X")
ylabel("Y")
zlabel("Z = exp(-(X^2 + Y^2))")Mesh vs Surf: Wireframe and Filled Surfaces
Equana provides two main 3D surface functions:
mesh(X, Y, Z): Creates a wireframe view showing only grid linessurf(X, Y, Z): Creates a filled surface with colored faces
Wireframe plots are useful for:
- Seeing through the surface to understand its structure
- Dense grids where filled surfaces would be cluttered
- Quickly previewing data before final visualization
# Compare mesh() wireframe vs surf() filled surface
(X, Y) = meshgrid(linspace(-2, 2, 14), linspace(-2, 2, 14))
Z = sin(sqrt(X.^2 + Y.^2))
# First, show wireframe mesh
mesh(X, Y, Z)
title("mesh() - Wireframe View")
xlabel("X")
ylabel("Y")
zlabel("Z")# Same surface with surf() - filled and colored
surf(X, Y, Z)
title("surf() - Filled Surface")
xlabel("X")
ylabel("Y")
zlabel("Z")
println("The mesh and surf variables persist across cells.")
println("You can modify X, Y, Z and re-run to see different surfaces.")The peaks() Function
The peaks() function generates a classic test surface — a combination of Gaussian peaks and valleys. It evaluates the formula:
This creates an interesting landscape with three peaks and three valleys, perfect for demonstrations and testing visualization capabilities.
# The peaks() function - classic test surface
# Default creates a 49x49 grid
surf(peaks())
title("peaks() - Classic Test Surface")
xlabel("X")
ylabel("Y")
zlabel("Z")# peaks(n) creates an n×n surface
# Smaller values render faster, larger values are smoother
surf(peaks(30))
title("peaks(30) - 30x30 Grid")
println("Try changing the grid size:")
println(" peaks(15) - faster, coarser")
println(" peaks(50) - slower, smoother")# Get coordinate matrices with (X, Y, Z) = peaks(n)
# Useful when you need the actual coordinate values
(X, Y, Z) = peaks(25)
surf(X, Y, Z)
title("peaks() with Coordinates")
xlabel("X")
ylabel("Y")
zlabel("Z")
# Display the range of coordinates
println("X range: " + string(min(X)) + " to " + string(max(X)))
println("Y range: " + string(min(Y)) + " to " + string(max(Y)))
println("Z range: " + string(min(Z)) + " to " + string(max(Z)))Mathematical Surfaces
Let's explore some classic mathematical surfaces. These demonstrate how to translate mathematical formulas into code using element-wise operations.
2D Sinc Function
The sinc function creates a classic ripple pattern used in signal processing and optics:
# 2D sinc function: sin(r)/r where r = sqrt(x^2 + y^2)
# Creates a ripple pattern emanating from the origin
(X, Y) = meshgrid(linspace(-8, 8, 41), linspace(-8, 8, 41))
# Compute radial distance from origin
R = sqrt(X.^2 + Y.^2) + 0.001 # add small value to avoid division by zero
# Sinc function
Z = sin(R) ./ R
surf(X, Y, Z)
title("2D Sinc Function")
xlabel("X")
ylabel("Y")
zlabel("sin(r)/r")Saddle Surface (Hyperbolic Paraboloid)
The hyperbolic paraboloid is a classic saddle shape. It curves upward in one direction and downward in the perpendicular direction, used in architecture and mathematics.
# Saddle surface (hyperbolic paraboloid)
# z = x^2 - y^2
(X, Y) = meshgrid(linspace(-2, 2, 27), linspace(-2, 2, 27))
Z = X.^2 - Y.^2
surf(X, Y, Z)
title("Saddle Surface")
xlabel("X")
ylabel("Y")
zlabel("Z = X^2 - Y^2")
println("The saddle curves up along x-axis and down along y-axis.")
println("It has a saddle point at the origin (0, 0, 0).")Ripple Effect
Combining trigonometric and exponential functions creates wave patterns that decay with distance from the origin:
# Ripple pattern with exponential decay
# Simulates a wave emanating from center
(X, Y) = meshgrid(linspace(-5, 5, 51), linspace(-5, 5, 51))
R = sqrt(X.^2 + Y.^2)
# Cosine wave multiplied by exponential decay
Z = cos(R * 2) .* exp(-R / 3)
surf(X, Y, Z)
title("Ripple Effect")
xlabel("X")
ylabel("Y")
zlabel("Amplitude")
println("Wave amplitude decreases with distance from origin.")Multiple Gaussian Peaks
You can create complex landscapes by summing multiple Gaussian functions at different locations:
# Surface with multiple Gaussian peaks
# Each peak is centered at a different location
(X, Y) = meshgrid(linspace(-3, 3, 41), linspace(-3, 3, 41))
# Peak 1: centered at (1, 1), height 1.0
Z1 = exp(-((X - 1).^2 + (Y - 1).^2))
# Peak 2: centered at (-1, -1), height 0.8
Z2 = exp(-((X + 1).^2 + (Y + 1).^2)) * 0.8
# Peak 3: centered at (1, -1), height 0.6
Z3 = exp(-((X - 1).^2 + (Y + 1).^2)) * 0.6
# Sum all peaks
Z = Z1 + Z2 + Z3
surf(X, Y, Z)
title("Multiple Gaussian Peaks")
xlabel("X")
ylabel("Y")
zlabel("Height")Advanced Surfaces
Paraboloid
A paraboloid is a bowl-shaped surface defined by :
# Paraboloid: z = 1 - x^2 - y^2
# Creates a bowl shape (inverted parabola in 3D)
(X, Y) = meshgrid(linspace(-1, 1, 26), linspace(-1, 1, 26))
Z = 1 - X.^2 - Y.^2
surf(X, Y, Z)
title("Paraboloid")
xlabel("X")
ylabel("Y")
zlabel("Z = 1 - X^2 - Y^2")
println("Maximum at origin: z = 1")
println("Zero contour forms a circle of radius 1")Monkey Saddle
The monkey saddle is a fun mathematical surface with three valleys — supposedly designed for a monkey with a tail!
# Monkey saddle: z = x^3 - 3xy^2
# Has three valleys (for two legs and a tail!)
(X, Y) = meshgrid(linspace(-1.5, 1.5, 31), linspace(-1.5, 1.5, 31))
Z = X.^3 - 3 * X .* Y.^2
surf(X, Y, Z)
title("Monkey Saddle")
xlabel("X")
ylabel("Y")
zlabel("Z = X^3 - 3XY^2")
println("Three-fold symmetry: three peaks and three valleys.")Rosenbrock Function
The Rosenbrock function is a classic benchmark for optimization algorithms. It has a narrow, curved valley that's easy to find but hard to navigate:
The global minimum is at where .
# Rosenbrock function - optimization benchmark
# f(x,y) = (1-x)^2 + 100(y-x^2)^2
# Global minimum at (1, 1) where f = 0
(X, Y) = meshgrid(linspace(-1.5, 1.5, 31), linspace(-0.5, 2, 26))
Z = (1 - X).^2 + 100 * (Y - X.^2).^2
# Log scale helps visualize the narrow valley
Z_log = log(Z + 1)
surf(X, Y, Z_log)
title("Rosenbrock Function (log scale)")
xlabel("X")
ylabel("Y")
zlabel("log(f+1)")
println("Minimum at (1, 1) - shown as the dark spot in the valley.")
println("The narrow curved valley makes this hard for optimizers.")Tips and Best Practices
Grid Resolution
The number of points in linspace controls surface smoothness vs. rendering speed:
| Points | Effect |
|---|---|
11 | Coarse, fast rendering |
26 | Good balance |
51 | Smooth, slower |
101 | Very smooth, may be slow |
Element-wise Operations
Always use .*, ./, and .^ for operations on meshgrid outputs. These apply element-by-element:
| Operation | Meaning |
|---|---|
X .* Y | Element-wise multiply |
X ./ Y | Element-wise divide |
X .^ 2 | Element-wise power |
Interactive Controls
- Rotate: Click and drag to rotate the 3D view
- Zoom: Scroll wheel or pinch to zoom
- Pan: Right-click drag (or use toolbar)
# Experiment with grid resolution
# Change n to see the effect on smoothness
n = 17 # Try: 11 (coarse), 17, 31 (fine)
(X, Y) = meshgrid(linspace(-2, 2, n), linspace(-2, 2, n))
Z = sin(X) .* cos(Y)
surf(X, Y, Z)
title("Grid Resolution: " + string(n) + " x " + string(n))
xlabel("X")
ylabel("Y")
zlabel("sin(X) * cos(Y)")
println("Grid dimensions: " + string(n) + " x " + string(n))
println("Total points: " + string(n * n))Summary
You've learned to create 3D surface visualizations in Equana:
Key Functions
| Function | Purpose |
|---|---|
surf(Z) | Surface from matrix |
surf(X, Y, Z) | Surface with coordinates |
mesh(X, Y, Z) | Wireframe surface |
meshgrid(x, y) | Create coordinate grids |
peaks(n) | Classic test surface |
Mathematical Surfaces Covered
- Gaussian — Bell curve:
- Sinc — Ripple:
- Saddle — Hyperbolic paraboloid:
- Paraboloid — Bowl:
- Monkey Saddle — Three valleys:
- Rosenbrock — Optimization benchmark
Best Practices
- Use
meshgridto create coordinate grids - Always use element-wise operators (
.*,./,.^) - Choose grid resolution based on smoothness vs. speed needs
- Add labels with
title,xlabel,ylabel,zlabel
Further Exploration
Try modifying the code cells above to:
- Change function parameters
- Combine multiple functions
- Adjust grid resolution
- Switch between
meshandsurf