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

8.3 KiB

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:

// 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

  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)