Detailed technical documentation for the Mathematica / Wolfram Language surface-route solver included in this repository.
The SurfaceGeodesicPlotter project is a Wolfram Language tool for computing and visualizing a shortest route constrained to an explicit surface of the form:
z = f(x, y)The project provides:
- a reusable Wolfram package:
SurfaceGeodesicPlotter.wl - a notebook launcher:
RUN_SurfaceGeodesicPlotter.nb - an interactive GUI app:
SurfaceGeodesicPlotterApp[] - a programmatic plotting and solving function:
SurfaceGeodesicPlotter[...]
At a high level, the tool:
- accepts a surface expression
f(x, y) - accepts two endpoints in the
x-ydomain - lifts those points onto the surface
- numerically approximates a shortest route that stays on the surface
- renders the surface, the route, and endpoint markers
- reports the final surface distance and endpoint coordinates
This is not a symbolic differential-geometry solver. It is a practical numerical approximation designed to be usable from a notebook UI.
The main files for this project are:
SurfaceGeodesicPlotter.wl- main Wolfram package
- contains the public API, numerical solver, validation, rendering, and app UI
RUN_SurfaceGeodesicPlotter.nb- notebook launcher
- loads the package from the same directory and opens the interactive app
README.md- short repo overview
Open:
RUN_SurfaceGeodesicPlotter.nb
Then evaluate the input cell with Shift+Enter.
That notebook runs:
Get[FileNameJoin[{NotebookDirectory[], "SurfaceGeodesicPlotter.wl"}]];
SurfaceGeodesicPlotter`SurfaceGeodesicPlotterApp[]If you want to use the package directly:
Get["/absolute/path/to/SurfaceGeodesicPlotter.wl"]Then:
SurfaceGeodesicPlotter`SurfaceGeodesicPlotterApp[]or:
SurfaceGeodesicPlotter`SurfaceGeodesicPlotter[
Sin[x] Cos[y],
{x, -3, 3},
{y, -3, 3},
{-2, -1.5},
{2, 1.5}
]Given an explicit surface z = f(x, y) and two points (x1, y1) and (x2, y2) in the domain, the project computes a route that approximately minimizes travel distance along the surface itself.
This matters because the shortest straight segment in 3D space usually does not lie on the surface. The project instead seeks a surface-constrained route.
- explicit surfaces only:
z = f(x, y) - real-valued surfaces
- numeric
xandydomain ranges - numeric endpoints in the
x-yplane - smooth or reasonably differentiable surfaces
- implicit surfaces such as
x^2 + y^2 + z^2 = 1 - parametric surfaces
- surfaces defined using variables other than
xandy - guaranteed exact geodesics
- formal proofs of global optimality
Main programmatic interface:
SurfaceGeodesicPlotter[
f,
{x, xmin, xmax},
{y, ymin, ymax},
{x1, y1},
{x2, y2},
opts
]It:
- validates the input
- computes the route
- renders the surface and route
- returns a formatted Wolfram expression suitable for notebook display
Interactive notebook UI.
It exposes the main controls for:
- surface expression
- domain bounds
- endpoints
- solver quality parameters
The package exposes several options through Options[SurfaceGeodesicPlotter].
| Option | Default | Meaning |
|---|---|---|
AxesLabel |
Automatic |
Axis labels for the rendered 3D plot |
PlotLabel |
Automatic |
Plot title above the 3D surface |
PlotPoints |
55 |
Base surface sampling density for Plot3D |
MaxRecursion |
2 |
Additional adaptive surface refinement for Plot3D |
Mesh |
None |
Surface mesh control; internally the renderer replaces None with a readable mesh for UI output |
BoxRatios |
{1,1,0.6} |
Aspect ratio of the 3D box |
PlotTheme |
"Scientific" |
Exposed for compatibility, though the renderer currently applies a custom visual style |
ImageSize |
Large |
Plot image size |
PerformanceGoal |
"Quality" |
Rendering performance/quality tradeoff |
PlotRange |
All |
Range used for display |
PlotStyle |
Directive[Opacity[0.9], ColorData["LakeColors"][0.58]] |
Base surface styling option |
RouteStyle |
red thick line directive | Style for the highlighted route |
StartPointStyle |
green point directive | Style for point A |
EndPointStyle |
blue point directive | Style for point B |
| Option | Default | Meaning |
|---|---|---|
InteriorControlPoints |
3 |
Number of free Bezier interior control points |
PathSamples |
180 |
Number of samples used to resample the final route for display and final length estimate |
OptimizationSamples |
40 |
Number of samples used in the arc-length objective during optimization |
MaxIterations |
250 |
Iteration cap passed to NMinimize |
The app UI groups inputs into four cards.
f(x, y)- text input
- must be valid Wolfram syntax
- examples:
Sin[x] Cos[y]x^2 + y^2Exp[-0.1 (x^2 + y^2)] Sin[3 x]
x rangey range
These define the rectangular x-y domain used for:
- endpoint validation
- route control-point constraints
- surface plotting
Point A (x, y)Point B (x, y)
These are the two endpoints in the domain plane. The app computes the corresponding heights:
z1 = f(x1, y1)
z2 = f(x2, y2)Interior controls- controls route flexibility
- larger values allow more path bending, but increase optimization difficulty
Optimization samples- controls objective approximation fidelity
- larger values usually improve quality, but make the solve slower
Path samples- controls route smoothness in the final display
Max iterations- cap for the optimization routine
Compute Route- parses input
- validates bounds and endpoints
- solves the numerical problem
- renders the output
Load Example- resets the app to a known working example
The surface is interpreted as:
The tangent vectors are:
This induces the first fundamental form:
If the route in parameter space is:
then the lifted surface route is:
and the arc-length functional becomes:
This is the quantity the project minimizes approximately.
The route is not optimized as a completely free function. Instead, it is approximated by a finite-dimensional Bezier curve in the x-y domain.
The project uses Bernstein polynomials because they provide:
- fixed endpoints
- smooth interpolation
- a compact finite-dimensional parameterization
- easy control through a small set of interior control points
The control-point list is:
{startPoint, interiorPoint1, interiorPoint2, ..., endPoint}The code builds:
Sum[BernsteinBasis[n, i, t] controlPoints[[i + 1]], {i, 0, n}]where n = Length[controlPoints] - 1.
This means the optimization variables are the interior control points only. The endpoints stay fixed.
The continuous arc-length integral is approximated numerically.
The project uses a trapezoidal rule through:
buildTrapezoidObjective[integrand_, sampleCount_, t_]Algorithm:
- create
sampleCountequally spacedtvalues in[0,1] - evaluate the integrand at those points
- apply trapezoidal weights
- sum the weighted values
This produces a deterministic, simple, and fast objective suitable for NMinimize.
The project solves the finite-dimensional problem with:
NMinimize[..., Method -> {"NelderMead", "RandomSeed" -> 1337}]The route objective can be:
- nonlinear
- nonconvex
- difficult to differentiate robustly after substitution and numeric evaluation
NelderMead is a practical derivative-free method that behaves reasonably for this moderate-dimensional optimization problem.
For InteriorControlPoints -> k, the variables are:
{cx[1], cy[1], ..., cx[k], cy[k]}Every control point is constrained to remain inside the rectangular domain:
xmin <= cx[i] <= xmax
ymin <= cy[i] <= ymaxThis keeps the route parameterization inside the intended plotting area.
After optimization:
- the Bezier curve is resampled using
PathSamples - each sample point
(x_i, y_i)is lifted to 3D:(x_i, y_i, f(x_i, y_i))
- the surface route length is recomputed from the 3D points
The final route length shown to the user is therefore a polyline approximation of the optimized path after lifting onto the surface.
The rendering step builds two main graphics layers.
Built with Plot3D.
Features:
- custom color blend across height
- visible but light mesh
- explicit
x,y,zaxis labels - controlled camera angle
- box, ticks, boundary style, and lighting for readability
Built with Graphics3D.
Features:
- thick dark under-tube
- brighter inner tube for route visibility
- endpoint spheres
- labeled markers
AandB
This double-layer route styling is intentional. A plain line often looks like it floats above the surface. Using a tube with stronger shading makes the route read as physically sitting on the surface.
The project validates input in several stages.
The UI parses the text with:
ToExpression[exprInput, InputForm, HoldComplete]It then normalizes user symbols so typed x and y match the solver’s internal symbols.
This was added specifically to avoid notebook-context symbol issues.
Only x and y are allowed as free variables.
Expressions containing other variables such as z, u, or t are rejected.
The project checks:
- both bounds are numeric
- min and max are ordered internally if needed
Each endpoint must:
- be a numeric pair
- lie inside the domain rectangle
The project evaluates:
f(x1, y1), f(x2, y2)and rejects endpoints if the surface does not return a finite real number.
This catches:
- complex values
Indeterminate- infinities
- undefined expressions
The solver computes D[f, x] and D[f, y]. If that fails, the route is rejected.
If NMinimize does not produce the expected numeric result and variable rules, the app reports an optimization failure instead of returning broken graphics.
If point A and point B are the same within a small tolerance:
- the route is a single point
- the route length is
0 - the solver skips optimization
If InteriorControlPoints -> 0, the project uses the straight segment in the x-y plane, lifts it onto the surface, and computes the resulting surface path length.
This is useful for:
- debugging
- fast previews
- comparing a naive path against optimized paths
The main performance controls are:
InteriorControlPointsOptimizationSamplesPathSamplesMaxIterations
Good for experimentation:
InteriorControlPoints -> 2OptimizationSamples -> 24PathSamples -> 120MaxIterations -> 120
Default-ish usage:
InteriorControlPoints -> 3OptimizationSamples -> 40PathSamples -> 180MaxIterations -> 250
Use for difficult surfaces:
InteriorControlPoints -> 4OptimizationSamples -> 56or72PathSamples -> 240or320MaxIterations -> 400or600
Higher settings help only up to a point. If the surface is extremely oscillatory or very steep, the optimization landscape becomes much harder.
This project is intentionally practical, but it has limitations.
- not an exact geodesic solver
- no global-optimum guarantee
- route family limited by Bezier parameterization
- route quality depends on the number of interior control points
- explicit surfaces only
- domain is rectangular in
x-y - no obstacles, holes, or excluded regions
- no singularity-aware routing beyond numeric failure checks
- endpoints are typed manually, not dragged on the surface
- there are no built-in camera presets yet
- there is no export button for route data
Sin[x] Cos[y]x^2 + y^2Exp[-0.08 (x^2 + y^2)] (Sin[4 x] + Cos[4 y]) + 0.12 Sin[x y]0.015 (x^2 + y^2)^2 - 0.35 (x^2 + y^2) + 0.6 Sin[2 x] Cos[2 y]The package is organized into several logical layers.
realFiniteNumberQnumericRealValuenormalizeIntervalnormalizePointmakeFailurenormalizeExpressionSymbolsparseAppExpression
bernsteinCurveExpressionbuildTrapezoidObjectivepathLengthFromPointscomputeSurfaceGeodesicResult
displayExpressionForUIrouteSummaryPanelrouteOverlayrenderGeodesicResultuiSectionCardstatusMessagePanel
buildAppResultSurfaceGeodesicPlotterApp