8.3 KiB
RocketTools Architecture
Overview
RocketTools is a single-page application (SPA) built with React + Vite, organized into logical layers:
- Pages — Route-level components
- Components — Reusable UI elements
- Hooks — State management & business logic
- Engine — Core calculations & algorithms
- 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 overviewSolver.jsx— Equation solver workspaceEnginePage.jsx— Engine design toolsRocketPage.jsx— Vehicle design toolsTrajectoryPage.jsx— Flight simulationDocsPage.jsx— In-app documentationKnowledgebaseEquationsPage.jsx— Equation referenceKnowledgebaseFuelsPage.jsx— Propellant databaseKnowledgebaseAblativesPage.jsx— Ablative materials
Components (src/components/)
Pure, composable UI elements that accept props and render JSX.
Solver Components:
Workspace.jsx— Drop zone for constraint cardsVariablePalette.jsx— Draggable variable listVariableCard.jsx— Input field with unit conversionResultsPanel.jsx— Results table display
Engine/Rocket Components:
rocket/RocketModel3D.jsx— Three.js 3D scenetrajectory/TrajectoryPlot.jsx— Canvas flight path charttrajectory/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:
// 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):
// 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):
// 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
- Memoization — Expensive calculations in
useMemo() - Lazy Loading — Routes loaded on-demand (React Router)
- Canvas Rendering — 60 FPS for trajectory animation
- 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
- Create
src/pages/NewPage.jsx - Add route in
App.jsx - Add NavLink if needed
Adding a New Feature Module
- Create hook:
src/hooks/useNewFeature.js - Create components:
src/components/NewFeature*.jsx - Add calculations to
src/engine/if needed - Create page:
src/pages/NewFeaturePage.jsx
Adding Material Properties
- Edit
src/engine/knowledgebaseData.js - Add to
FUELS,ABLATIVES, orSTRUCTURAL_MATERIALSarray - Auto-appears in UI
Last Updated: 2025-02 | Status: Current (v3)