Files
rocketry/docs/ARCHITECTURE.md
2026-03-04 20:29:19 +00:00

309 lines
8.3 KiB
Markdown

# RocketTools Architecture
## Overview
RocketTools is a single-page application (SPA) built with **React + Vite**, organized into logical layers:
1. **Pages** — Route-level components
2. **Components** — Reusable UI elements
3. **Hooks** — State management & business logic
4. **Engine** — Core calculations & algorithms
5. **App.jsx** — Router configuration
## Layer Responsibilities
### Pages (`src/pages/`)
Each page is a route-level component that:
- Uses one or more custom hooks for state
- Composes domain-specific components
- Manages local UI state (modals, tabs, etc.)
**Pages:**
- `Home.jsx` — Landing page with feature overview
- `Solver.jsx` — Equation solver workspace
- `EnginePage.jsx` — Engine design tools
- `RocketPage.jsx` — Vehicle design tools
- `TrajectoryPage.jsx` — Flight simulation
- `DocsPage.jsx` — In-app documentation
- `KnowledgebaseEquationsPage.jsx` — Equation reference
- `KnowledgebaseFuelsPage.jsx` — Propellant database
- `KnowledgebaseAblativesPage.jsx` — Ablative materials
### Components (`src/components/`)
Pure, composable UI elements that accept props and render JSX.
**Solver Components:**
- `Workspace.jsx` — Drop zone for constraint cards
- `VariablePalette.jsx` — Draggable variable list
- `VariableCard.jsx` — Input field with unit conversion
- `ResultsPanel.jsx` — Results table display
**Engine/Rocket Components:**
- `rocket/RocketModel3D.jsx` — Three.js 3D scene
- `trajectory/TrajectoryPlot.jsx` — Canvas flight path chart
- `trajectory/TimelineBar.jsx` — Playback controls
### Hooks (`src/hooks/`)
Custom React hooks encapsulate domain state & logic.
**State Hooks:**
```
useSolver()
├─ variables: Map<id, value>
├─ constraints: Map<id, constraint>
├─ dispatch(action)
└─ results: Map<id, solvedValue>
useEngineDesign()
├─ engineInputs: {chamberPressure, fuels, ...}
├─ engineResults: {thrust, isp, ...}
├─ setEngineInputs()
└─ structureResults: {wallThickness, mass, ...}
useRocketDesign()
├─ rocketInputs: {outerRadius, tankConfig, ...}
├─ rocketResults: {totalMass, twr, ...}
└─ setRocketInputs()
useTrajectory()
├─ config: {vehicle, engine, atmosphere, ...}
├─ playback: {time, speed, isPlaying}
├─ events: [{type, time, data}]
└─ runSimulation()
```
### Engine (`src/engine/`)
Pure JavaScript modules with **zero React dependencies**. These can be:
- Unit tested independently
- Reused in Node.js or other environments
- Debugged without UI overhead
**Module Breakdown:**
| File | Purpose |
|------|---------|
| `variables.js` | Variable catalog (name, unit, type) |
| `equations.js` | Equation definitions + solvers |
| `solver.js` | Constraint propagation algorithm |
| `numerics.js` | Math utilities (bisection, Mach) |
| `engineDesignCalcs.js` | Combustion, ablation, structure |
| `rocketDesignCalcs.js` | Tank geometry, mass budget |
| `trajectoryCalcs.js` | RK4 integrator, event detection |
| `knowledgebaseData.js` | Material & fuel databases |
## Data Flow Patterns
### Solver Workflow
```
User Input
[VariableCard updates constraint]
dispatch(setConstraint) → useSolver
solver.solve() → [greedy propagation]
useSolver state updates
[VariableCard/ResultsPanel re-render]
```
### Engine Design Workflow
```
User Input (EnginePage)
useEngineDesign.setEngineInputs()
engineDesignCalcs.calcCombustion()
+ engineDesignCalcs.calcCooling()
+ engineDesignCalcs.calcStructure()
useSolver state updates (memoized)
[EnginePage, RocketPage re-render]
```
### Rocket Design Workflow
```
User Input (RocketPage)
useRocketDesign.setRocketInputs()
rocketDesignCalcs.calcTankGeometry()
+ rocketDesignCalcs.calcMassBudget()
+ rocketDesignCalcs.noseConeProfile()
useRocketDesign state updates (memoized)
[RocketModel3D, RocketPage re-render]
```
### Trajectory Simulation Workflow
```
User Config (TrajectoryPage)
useTrajectory.runSimulation()
trajectoryCalcs.runSimulation()
[RK4 integrator loop]
[Event detection]
states[] + events[] returned
useTrajectory state updates
[TrajectoryPlot, TimelineBar re-render]
```
## Key Design Decisions
### 1. Separation of Concerns
- **Engine layer**: Pure math, no React
- **Hooks layer**: State management, memoization, side effects
- **Component layer**: UI rendering only
- **Page layer**: Route composition, layout
**Benefit**: Engine code is testable, reusable, and maintainable independently of React.
### 2. Constraint Solver as Core
The solver uses **greedy constraint propagation**:
- User sets constraint on a variable
- Solver finds equations referencing that variable
- Solves for unknowns using math or bisection
- Propagates newly-computed values (recursive)
**Benefit**: Intuitive UI (users see automatic computation), flexible equation network.
### 3. Three.js via React Three Fiber
Rather than direct Three.js imperative API:
- Use `<Canvas>`, `<Mesh>`, etc. as React components
- Hooks like `useFrame()` for animations
- Drei helpers for common geometries
**Benefit**: Declarative, composable, integrates with React state.
### 4. Canvas for Trajectory Plotting
HTML5 Canvas instead of SVG:
- Handles 1000s of points efficiently
- Responsive to window resize
- Custom click/hover interactions
**Benefit**: Performance, flexibility.
## Memoization Strategy
Use `useMemo()` to avoid re-computation:
```javascript
// Hook definition
const engineResults = useMemo(() => {
return calcCombustion(engineInputs) // only runs when engineInputs changes
}, [engineInputs])
```
**Rule**: Memoize expensive calculations; skip for cheap operations.
## State Management (No Redux)
React hooks provide sufficient state management for this app:
- `useState()` for local UI state
- Custom hooks + `useCallback()` for complex logic
- `useMemo()` for expensive computations
- Props drilling is acceptable for this size
**Alternative**: Could migrate to Redux/Zustand if app grows significantly.
## 3D Rendering Pipeline
```
RocketModel3D.jsx (R3F Canvas)
[useRocketDesign hook provides geometry]
TankSection, NoseCone components
THREE.CylinderGeometry, SphereGeometry, LatheGeometry
Material (MeshStandardMaterial)
Lighting, OrbitControls
Canvas rendered to DOM
```
## Testing Strategy
No tests currently, but recommended:
**Unit Tests** (Jest):
```javascript
// src/engine/__tests__/solver.test.js
import { solve } from '../solver.js'
test('solves linear equation', () => {
const eqs = [{var: 'x', fn: (x) => 2*x - 5}]
const result = solve(eqs, {y: 10})
expect(result.x).toBeCloseTo(2.5)
})
```
**Integration Tests** (React Testing Library):
```javascript
// src/pages/__tests__/Solver.test.jsx
import { render, screen } from '@testing-library/react'
import Solver from '../Solver'
test('Solver page renders', () => {
render(<Solver />)
expect(screen.getByText(/Equation Solver/i)).toBeInTheDocument()
})
```
## Performance Considerations
1. **Memoization** — Expensive calculations in `useMemo()`
2. **Lazy Loading** — Routes loaded on-demand (React Router)
3. **Canvas Rendering** — 60 FPS for trajectory animation
4. **Variable Updates** — Solver propagation is greedy (stops early)
**Potential Bottlenecks:**
- Large variable networks (100+ equations)
- Complex 3D geometries (reduce polygon count)
- Trajectory with 1000s of timesteps (reduce accuracy or cache)
## File Organization Best Practices
- **One component per file** (except tiny helpers)
- **Colocate tests** with source (`__tests__` subfolder)
- **Group by domain** (rocket, trajectory, solver) not by type
- **Keep hooks small** (< 50 lines of logic)
- **Engine code** has zero dependencies on React or UI
## Extension Points
### Adding a New Page
1. Create `src/pages/NewPage.jsx`
2. Add route in `App.jsx`
3. Add NavLink if needed
### Adding a New Feature Module
1. Create hook: `src/hooks/useNewFeature.js`
2. Create components: `src/components/NewFeature*.jsx`
3. Add calculations to `src/engine/` if needed
4. Create page: `src/pages/NewFeaturePage.jsx`
### Adding Material Properties
1. Edit `src/engine/knowledgebaseData.js`
2. Add to `FUELS`, `ABLATIVES`, or `STRUCTURAL_MATERIALS` array
3. Auto-appears in UI
---
**Last Updated**: 2025-02 | **Status**: Current (v3)