309 lines
8.3 KiB
Markdown
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)
|