Introduction
Welcome to the Camera Optics Calculator documentation!
This application helps you calculate and visualize camera system performance, including field of view (FOV), spatial resolution, and depth of field calculations.
What You Can Do
- Calculate Field of View: Determine the angular and linear FOV for any camera/lens combination
- Spatial Resolution: Calculate pixels per millimeter (PPM) and ground sample distance (GSD)
- Depth of Field: Calculate near/far limits and total DOF for given aperture settings
- Compare Systems: Overlay and compare multiple camera configurations
- CLI or GUI: Use the tool via command-line or graphical interface
Built With
- Rust - Fast, safe backend calculations via Tauri
- TypeScript - Type-safe frontend UI
- Vite - Modern build tooling
- Canvas API - Real-time visualization
Get Started
Head to the Quick Start guide to begin!
Quick Start
Installation
GUI Application
# Clone the repository
git clone https://github.com/cramke/camera-optics.git
cd camera-optics
# Install frontend dependencies
pnpm install
# Run in development mode
pnpm tauri:dev
# Build for production
pnpm tauri:build
CLI Tool
The CLI is included with the application:
cd src-tauri
cargo build --release --bin camera-optics-cli
# The binary will be at:
# target/release/camera-optics-cli
First Calculation
Using the GUI
- Launch the app:
pnpm tauri:dev - Enter sensor dimensions (e.g., 36×24mm for full frame)
- Enter pixel resolution (e.g., 6000×4000)
- Enter focal length (e.g., 50mm)
- Enter working distance (e.g., 5000mm = 5 meters)
- Click "Calculate FOV"
Using the CLI
Calculate FOV for a full-frame camera with 50mm lens at 5m distance:
cargo run --bin camera-optics-cli -- fov \
-w 36 -H 24 \
-x 6000 -y 4000 \
-f 50 \
-d 5000
Next Steps
- Learn about the GUI interface
- Explore CLI commands
- Understand optical calculations
GUI Application
The graphical interface provides an intuitive way to calculate and compare camera systems.
Main Interface
The GUI is divided into several sections:
Camera System Input
Enter your camera and lens specifications:
- Sensor Width/Height (mm): Physical sensor dimensions
- Pixel Width/Height: Sensor resolution in pixels
- Focal Length (mm): Lens focal length
- Working Distance (mm): Distance to subject
- System Name: Optional identifier for comparison
Calculation Buttons
- Calculate FOV: Compute field of view for current system
- Auto-Calculate: Automatically recalculates when you change focal length (with 300ms debounce)
Results Display
After calculation, you'll see:
- Field of View: Horizontal and vertical (angular and linear)
- Spatial Resolution: Pixels per mm (PPM) and ground sample distance (GSD)
- Coverage Area: Total area covered by the sensor
System Comparison
- Add System: Saves current configuration for comparison
- Compare Mode: Overlays multiple systems on visualization
- Edit System: Modify saved configurations
- Delete System: Remove from comparison
Visualization
Real-time canvas visualization shows:
- FOV rectangle with dimensions
- Sensor aspect ratio
- Multiple systems overlaid for comparison
- Color-coded systems
Tips
- Use Auto-Calculate for quick focal length exploration
- Save systems before changing values to compare configurations
- Hover over visualizations to see system details
- Values are validated with realistic min/max constraints
CLI Tool
The command-line interface provides scriptable access to all calculations.
Installation
cd src-tauri
cargo build --release --bin camera-optics-cli
The binary will be at target/release/camera-optics-cli.
Commands
FOV Calculation
camera-optics-cli fov \
--sensor-width 36 \
--sensor-height 24 \
--pixel-width 6000 \
--pixel-height 4000 \
--focal-length 50 \
--distance 5000 \
--name "Full Frame 50mm"
Short flags:
camera-optics-cli fov -w 36 -H 24 -x 6000 -y 4000 -f 50 -d 5000
Hyperfocal Distance
Find the focus distance where everything from half that distance to infinity is acceptably sharp:
camera-optics-cli hyperfocal \
--focal-length 50 \
--aperture 8
Depth of Field
Calculate near limit, far limit, and total DOF:
camera-optics-cli dof \
--distance 3000 \
--focal-length 50 \
--aperture 2.8
System Comparison
Compare common sensor formats:
camera-optics-cli compare --distance 5000 --presets
Compare custom systems:
camera-optics-cli compare \
--distance 5000 \
--systems "36,24,6000,4000,50,Full Frame" \
"23.5,15.6,6000,4000,35,APS-C"
Output Format
All commands output JSON by default for easy parsing:
{
"camera": {
"name": "Full Frame 50mm",
"sensor_width_mm": 36.0,
"sensor_height_mm": 24.0,
"pixel_width": 6000,
"pixel_height": 4000,
"focal_length_mm": 50.0
},
"fov_horizontal_deg": 39.6,
"fov_vertical_deg": 27.0,
"fov_width_mm": 3600.0,
"fov_height_mm": 2400.0,
"ppm_horizontal": 1.67,
"ppm_vertical": 1.67,
"gsd_mm": 0.6
}
Scripting Examples
Batch calculations
#!/bin/bash
for focal in 24 35 50 85; do
camera-optics-cli fov -w 36 -H 24 -x 6000 -y 4000 -f $focal -d 5000 \
> results_${focal}mm.json
done
Parse with jq
camera-optics-cli fov -w 36 -H 24 -x 6000 -y 4000 -f 50 -d 5000 | \
jq '.fov_width_mm'
System Comparison
Camera Optics Calculator - Usage Guide
Project Structure
src-tauri/
├── src/
│ ├── lib.rs # Tauri GUI library (shared optics module)
│ ├── main.rs # GUI binary entry point
│ ├── cli_commands.rs # CLI binary entry point
│ ├── gui_commands.rs # Tauri command wrappers
│ └── optics/ # Shared optical calculation library
│ ├── mod.rs # Module exports
│ ├── types.rs # Data structures (CameraSystem, FovResult)
│ └── calculations.rs # Pure calculation functions (FOV, DOF, etc.)
Building
Build both GUI and CLI:
cd src-tauri
cargo build --bins
Build release versions:
cargo build --bins --release
The binaries will be in:
- GUI:
target/debug/tauri-app(ortarget/release/tauri-app) - CLI:
target/debug/camera-optics-cli(ortarget/release/camera-optics-cli)
CLI Usage
Calculate Field of View
Calculate FOV and spatial resolution for a camera system:
camera-optics-cli fov \
-w 36 \ # Sensor width (mm)
-H 24 \ # Sensor height (mm)
-x 6000 \ # Horizontal pixels
-y 4000 \ # Vertical pixels
-f 50 \ # Focal length (mm)
-d 5000 \ # Working distance (mm)
-n "My Camera" # Optional name
Example output:
Full Frame 50mm: 36x24 mm sensor, 6000x4000 px (6.00x6.00 µm), 50 mm lens
FOV: 39.60° × 26.99° (3600.00 × 2400.00 mm @ 5000 mm)
Resolution: 1.667 ppm, GSD: 0.600 mm/px
Compare Multiple Systems
Compare common sensor formats:
camera-optics-cli compare -d 5000 --presets
Compares Full Frame, APS-C, and Micro 4/3 sensors at 5000mm distance.
Calculate Hyperfocal Distance
camera-optics-cli hyperfocal -f 50 -a 8 -c 0.03
Arguments:
-f: Focal length (mm)-a: F-number (aperture)-c: Circle of confusion (mm) - optional, defaults to 0.03
Calculate Depth of Field
camera-optics-cli dof -d 3000 -f 50 -a 2.8
Arguments:
-d: Object distance (mm)-f: Focal length (mm)-a: F-number (aperture)-c: Circle of confusion (mm) - optional, defaults to 0.03
GUI Usage (Tauri App)
Run in development mode:
pnpm tauri dev
Build for production:
pnpm tauri build
Available Tauri Commands (from TypeScript):
import { invoke } from '@tauri-apps/api/core';
// Calculate single camera FOV
const result = await invoke('calculate_camera_fov', {
camera: {
sensor_width_mm: 36,
sensor_height_mm: 24,
pixel_width: 6000,
pixel_height: 4000,
focal_length_mm: 50,
name: 'Full Frame',
},
distanceMm: 5000,
});
// Compare multiple cameras
const results = await invoke('compare_camera_systems', {
cameras: [camera1, camera2, camera3],
distanceMm: 5000,
});
// Calculate hyperfocal distance
const hyperfocal = await invoke('calculate_hyperfocal_distance', {
focalLengthMm: 50,
fNumber: 8,
cocMm: 0.03,
});
// Calculate depth of field
const dof = await invoke('calculate_depth_of_field', {
objectDistanceMm: 3000,
focalLengthMm: 50,
fNumber: 2.8,
cocMm: 0.03,
});
Common Sensor Sizes (Reference)
| Format | Width × Height (mm) | Common Pixel Counts |
|---|---|---|
| Full Frame | 36 × 24 | 6000×4000, 8000×5333 |
| APS-C | 23.5 × 15.6 | 6000×4000, 5184×3456 |
| Micro 4/3 | 17.3 × 13.0 | 5184×3888, 4608×3456 |
| 1" | 13.2 × 8.8 | 5472×3648 |
| 1/1.8" | 7.2 × 5.3 | 4000×3000 |
Optical Formulas Used
Field of View (Angular)
FOV = 2 × arctan(sensor_size / (2 × focal_length))
Field of View (Linear at distance)
FOV_linear = 2 × distance × tan(FOV_angular / 2)
Spatial Resolution
PPM (pixels per mm) = pixel_count / FOV_linear
GSD (ground sample distance) = FOV_linear / pixel_count
Hyperfocal Distance
H = (f² / (N × c)) + f
Where: f = focal length, N = f-number, c = circle of confusion
Depth of Field
Near limit: Dn = (H × s) / (H + (s - f))
Far limit: Df = (H × s) / (H - (s - f))
Where: H = hyperfocal distance, s = subject distance, f = focal length
Next Steps
For the GUI:
- Design the HTML interface with input forms
- Create Canvas/SVG visualization for FOV overlay
- Add TypeScript to connect UI to Rust commands
- Implement interactive comparison with multiple cameras
For the CLI:
- Add more presets (industrial cameras, smartphones, etc.)
- Export results to JSON/CSV
- Batch processing from configuration files
- Add lens distortion calculations
Development Tips
- All optical math goes in
optics/calculations.rs - Keep functions pure (no side effects)
- Add unit tests for formulas
- Tauri commands are just thin wrappers in
gui_commands.rs - CLI uses the same calculation functions, ensuring consistency
FOV Parameter Feature
Overview
The DORI Designer now supports Horizontal Field of View (FOV) as an input parameter constraint. This allows you to specify a desired FOV angle and see what camera configurations can achieve it while meeting your DORI distance requirements.
How It Works
FOV Relationship
FOV is mathematically related to focal length and sensor width through this formula:
FOV = 2 × arctan(sensor_width / (2 × focal_length))
When you specify a FOV value, it constrains the ratio between focal length and sensor width.
Usage in DORI Designer
- Enter your DORI target distances (Detection, Observation, Recognition, or Identification)
- Specify FOV constraint (optional):
- Enter a value in degrees (e.g., 60° for a moderate wide angle, 90° for very wide)
- The system will calculate valid focal length and sensor width ranges that maintain this FOV
- Combine with other constraints:
- FOV + Pixel Width: Calculates focal and sensor ranges maintaining both constraints
- FOV + Focal Length: Determines the exact sensor width needed
- FOV + Sensor Width: Determines the exact focal length needed
- View calculated ranges for unconstrained parameters
Examples
Example 1: Wide-angle surveillance
- Target: Identification at 10m
- Constraint: 90° horizontal FOV
- Result: Shows focal length range (2.6mm - 43.3mm) and matching sensor width range (3mm - 50mm)
Example 2: Narrow-angle monitoring
- Target: Recognition at 50m
- Constraints: 30° FOV + 1920 pixel width
- Result: Calculates specific focal and sensor ranges that satisfy both the FOV and DORI requirements
Example 3: No FOV constraint
- Target: Observation at 25m
- No FOV constraint
- Result: Shows FOV range possible (e.g., 7° to 74°) based on other parameter ranges
Implementation Details
Backend (Rust)
- Type:
horizontal_fov_deg: Option<f64>inParameterConstraintandDoriParameterRanges - Calculation:
calculate_dori_parameter_ranges()handles FOV constraints - Formula:
sensor = 2 × focal × tan(FOV/2)used to maintain relationship - Bounds: Focal length constrained so sensor stays within physical limits (3mm - 50mm)
Frontend (TypeScript/HTML)
- Input field: "Horizontal FOV (°)" in DORI Designer tab
- Clear button: Click × to make FOV a floating parameter
- Range display: Shows FOV range when not constrained
- Auto-update: Recalculates when FOV or other parameters change
Tests
Three comprehensive tests verify FOV constraint behavior:
test_dori_ranges_with_fov_constraint- FOV onlytest_dori_ranges_fov_and_pixel_constraint- FOV + pixelstest_dori_ranges_no_fov_constraint- FOV as output range
All tests verify that the FOV relationship is maintained across the calculated ranges.
Benefits
- More intuitive: Specify desired viewing angle directly
- Better design: Choose FOV based on scene coverage needs
- Reduces trial-and-error: System calculates compatible camera specs automatically
- Real-world workflow: Many users think in terms of FOV rather than focal length
Future Enhancements
- FOV visualization showing coverage area
- Common FOV presets (wide, normal, telephoto)
- Diagonal FOV calculation
- Vertical FOV display
API Reference
Tauri Commands
The frontend communicates with the Rust backend through Tauri commands.
calculate_fov
Calculate field of view for a camera system.
TypeScript:
import { invoke } from '@tauri-apps/api/core';
const result = await invoke('calculate_fov', {
camera: {
name: 'My Camera',
sensor_width_mm: 36,
sensor_height_mm: 24,
pixel_width: 6000,
pixel_height: 4000,
focal_length_mm: 50,
},
distance_mm: 5000,
});
Response:
{
camera: CameraSystem;
fov_horizontal_deg: number;
fov_vertical_deg: number;
fov_width_mm: number;
fov_height_mm: number;
ppm_horizontal: number;
ppm_vertical: number;
gsd_mm: number;
}
calculate_hyperfocal
Calculate hyperfocal distance.
TypeScript:
const result = await invoke('calculate_hyperfocal', {
focalLengthMm: 50,
aperture: 8,
});
Response:
{
hyperfocal_distance_mm: number;
}
calculate_dof
Calculate depth of field.
TypeScript:
const result = await invoke('calculate_dof', {
distanceMm: 3000,
focalLengthMm: 50,
aperture: 2.8,
});
Response:
{
near_limit_mm: number;
far_limit_mm: number;
total_dof_mm: number;
}
Data Types
CameraSystem
interface CameraSystem {
name: string;
sensor_width_mm: number;
sensor_height_mm: number;
pixel_width: number;
pixel_height: number;
focal_length_mm: number;
}
FovResult
interface FovResult {
camera: CameraSystem;
fov_horizontal_deg: number;
fov_vertical_deg: number;
fov_width_mm: number;
fov_height_mm: number;
ppm_horizontal: number;
ppm_vertical: number;
gsd_mm: number;
}
DofResult
interface DofResult {
near_limit_mm: number;
far_limit_mm: number;
total_dof_mm: number;
}
HyperfocalResult
interface HyperfocalResult {
hyperfocal_distance_mm: number;
}
Validation Constraints
All inputs are validated with these constraints:
const VALIDATION_CONSTRAINTS = {
sensorWidth: { min: 0.1, max: 200 }, // mm
sensorHeight: { min: 0.1, max: 200 }, // mm
pixelWidth: { min: 10, max: 100000 }, // pixels
pixelHeight: { min: 10, max: 100000 }, // pixels
focalLength: { min: 0.1, max: 10000 }, // mm
fov: { min: 0.1, max: 180 }, // degrees
distance: { min: 0.01, max: 100000 }, // mm
};
Frontend Architecture
Pattern: Layered Architecture with strict separation of concerns
src/
├── core/ # Domain layer (types, constants)
├── services/ # Service layer (API, state)
├── ui/ # Presentation layer (components)
└── main.ts # Entry point & orchestration
Structure
📦 core/ - Domain Layer (117 lines)
Pure domain logic with zero dependencies.
Files:
types.ts(37) - CameraSystem, FovResult, CameraWithResult, ReferenceObjectconstants.ts(74) - Reference objects, camera presets, system colorsindex.ts(6) - Module exports
Rule: No imports from other layers
🔌 services/ - Service Layer (110 lines)
Backend communication and state management.
Files:
api.ts(32) - Tauri IPC wrapper (calculateCameraFov, calculateFocalLengthFromFov)store.ts(72) - Observable state store for camera systemsindex.ts(6) - Module exports
Dependencies: core/ only
🎨 ui/ - Presentation Layer (406 lines)
DOM manipulation, rendering, and user interactions.
Files:
form.ts(118) - Form I/O, presets, validationresults.ts(46) - Results page renderingvisualization.ts(235) - Canvas FOV visualizationindex.ts(7) - Module exports
Dependencies: core/ + services/
🚀 main.ts - Entry Point (170 lines)
Application bootstrap, event handlers, workflow orchestration.
Dependencies: All layers
Dependency Flow
main.ts
↓
┌─────────┬───────────┬────────┐
│ core/ │ services/ │ ui/ │
│ (types) │ (api, │ (form, │
│ │ store) │ viz) │
└─────────┴───────────┴────────┘
Rules:
- ✅
core/→ No dependencies - ✅
services/→core/only - ✅
ui/→core/+services/ - ✅
main.ts→ All layers - ❌ No circular dependencies
Import Examples
✅ Correct:
// main.ts
import { calculateCameraFov } from "./services";
import { drawVisualization } from "./ui";
// ui/form.ts
import type { CameraSystem } from "../core/types";
import { store } from "../services/store";
// services/api.ts
import type { FovResult } from "../core/types";
❌ Wrong:
// core/types.ts
import { store } from "../services/store"; // ❌ Core can't depend on services
// services/api.ts
import { displayResult } from "../ui/results"; // ❌ Service can't depend on UI
Adding Features
New UI Component:
- Create in
ui/ - Import from
core/andservices/ - Export from
ui/index.ts
New Service:
- Create in
services/ - Import from
core/only - Export from
services/index.ts
New Type:
- Add to
core/types.ts(available everywhere)
Building
Prerequisites
- Rust 1.70+ - Install rustup
- Node.js 20+ - Install Node
- pnpm 9+ -
npm install -g pnpm
System Dependencies (Linux)
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libappindicator3-dev \
librsvg2-dev \
patchelf
Development Build
# Install frontend dependencies
pnpm install
# Run in development mode (hot reload)
pnpm tauri:dev
This starts:
- Vite dev server on http://localhost:1420
- Tauri window with the app
- Hot reload for frontend changes
- Rust recompilation on backend changes
Production Build
# Build optimized release
pnpm tauri:build
Output locations:
- Linux:
src-tauri/target/release/bundle/deb/ - Windows:
src-tauri/target/release/bundle/msi/ - macOS:
src-tauri/target/release/bundle/dmg/
CLI Only
cd src-tauri
cargo build --release --bin camera-optics-cli
# Binary: target/release/camera-optics-cli
Debug vs Release
Debug build (faster compile, slower runtime):
cargo build
Release build (slower compile, optimized):
cargo build --release
Build Flags
Disable warnings-as-errors
cargo build # No RUSTFLAGS set
Enable all warnings
RUSTFLAGS="-D warnings" cargo build
Troubleshooting
WebKit errors on Linux
Install missing system dependencies:
sudo apt-get install libwebkit2gtk-4.1-dev
pnpm not found
npm install -g pnpm
Tauri build fails
Clear cache and rebuild:
rm -rf node_modules target
pnpm install
pnpm tauri build
Testing
Running Tests
Frontend Tests (Vitest)
# Run all tests
pnpm test
# Run with UI
pnpm test:ui
# Generate coverage
pnpm test:coverage
Rust Tests
cd src-tauri
cargo test
Rust Tests with Coverage
cd src-tauri
cargo install cargo-llvm-cov
cargo llvm-cov test
Test Structure
Frontend
Tests are located in src/**/*.test.ts:
src/services/store.test.ts- Store CRUD operations (22 tests)
Example test:
import { describe, it, expect, beforeEach } from 'vitest';
import { Store } from './store';
describe('Store', () => {
it('should add new system', () => {
const store = new Store();
const system = {
/* ... */
};
const id = store.add(system);
expect(store.get(id)).toBeDefined();
});
});
Rust
Tests are embedded in source files with #[cfg(test)]:
#![allow(unused)] fn main() { #[cfg(test)] mod tests { use super::*; #[test] fn test_calculate_fov() { let camera = CameraSystem { /* ... */ }; let result = calculate_fov(&camera, 5000.0); assert!(result.fov_horizontal_deg > 0.0); } } }
CI Tests
Tests run automatically in GitHub Actions:
- Format Check:
cargo fmt --check,prettier --check - Lint:
cargo clippy, (TypeScript viatsc) - Build:
cargo build --release,pnpm build - Test:
cargo test,pnpm test - Coverage: Combined Rust + TypeScript
Writing Tests
Frontend Test Guidelines
- Unit tests for utilities and services
- Integration tests for Tauri commands (mock backend)
- Use
happy-domfor DOM testing - Mock Tauri APIs with
vi.mock()
Rust Test Guidelines
- Unit tests in same file as implementation
- Integration tests in
tests/directory - Test edge cases (zero, negative, infinity)
- Use
assert_eq!for exact matches - Use
assert!with tolerance for floats
Coverage Goals
- Minimum: 70% overall
- Target: 85%+ for critical paths
- Excluded: UI interaction code, error handling
View coverage:
- Frontend:
coverage/index.html - Rust: Terminal output from
cargo llvm-cov
Contributing
Thank you for considering contributing to Camera Optics Calculator!
Getting Started
- Fork the repository
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/camera-optics.git cd camera-optics - Create a branch:
git checkout -b feature/your-feature-name
Development Workflow
- Make your changes
- Format code:
cargo fmt pnpm prettier --write . - Lint:
cargo clippy pnpm build # TypeScript checking - Test:
cargo test pnpm test - Commit:
git add . git commit -m "feat: add new feature"
Commit Convention
Use Conventional Commits:
feat:New featurefix:Bug fixdocs:Documentationstyle:Formatting (no code change)refactor:Code restructuringtest:Adding testschore:Maintenance
Examples:
feat: add hyperfocal distance calculationfix: correct FOV calculation for wide angle lensesdocs: update API reference
Pull Request Process
- Update documentation if needed
- Add tests for new features
- Ensure CI passes (all checks must pass)
- Request review from maintainers
Code Style
TypeScript
- Use TypeScript strict mode
- Prefer
constoverlet - Use single quotes for strings
- 2-space indentation
- Max line length: 100 characters
Rust
- Follow
rustfmtdefaults - Use
clippyrecommendations - Document public APIs with
///comments - Prefer
?operator for error handling
Testing Requirements
- New features must include tests
- Bug fixes should include regression tests
- Maintain or improve coverage percentage
Security
If you discover a security vulnerability:
- Do not open a public issue
- Email the maintainer directly
- Provide detailed reproduction steps
Questions?
- Open an issue for discussion
- Check existing issues/PRs first
- Be respectful and constructive
We appreciate your contributions! 🎉