10 KiB
10 KiB
Contributing Guide
Getting Started
Prerequisites
- Node.js 18+
- npm or yarn
- Basic understanding of React, JavaScript
Setup
# Clone/navigate to project
cd rocketry
# Install dependencies
npm install
# Start dev server
npm run dev
# Open browser
# http://localhost:5173
Project Structure Overview
src/
├── engine/ # Pure math, zero React deps
├── hooks/ # State management
├── components/ # UI components
├── pages/ # Route pages
└── App.jsx # Router
See ARCHITECTURE.md for detailed breakdown.
Common Tasks
Adding a New Equation
File: src/engine/equations.js
-
Check if variables exist in
variables.js- If not, add them with
id,name,unit,category,type
- If not, add them with
-
Add equation object:
{
id: 'myEquation',
name: 'My Equation Name',
variables: ['var1', 'var2', 'var3'],
equation: 'var1 = var2 * var3', // for reference
solvers: {
var1: (inputs) => inputs.var2 * inputs.var3,
var2: (inputs) => inputs.var1 / inputs.var3,
var3: (inputs) => inputs.var1 / inputs.var2
}
}
- Test manually
- Go to Solver page
- Drag variables onto workspace
- Set constraints, verify solver computes correct values
For Transcendental Equations (Bisection)
import { bisection } from '../numerics.js'
{
id: 'machEquation',
solvers: {
machNumber: (inputs) => {
const fn = (M) => isentropicAreaRatio(M, inputs.gamma) - inputs.areaRatio
return bisection(fn, 0.1, 10, 1e-6)
}
}
}
Adding a Material to Knowledgebase
File: src/engine/knowledgebaseData.js
For fuels/oxidizers:
FUELS.push({
id: 'newFuel',
name: 'New Fuel',
formula: 'C8H18', // typical hydrocarbon
density: 800, // kg/m³
molecularWeight: 114,
properties: {
combustionTemp: 3500,
gamma: 1.25,
charVelocity: 1500 // m/s
}
})
For ablatives:
ABLATIVES.push({
id: 'newAblative',
name: 'New Ablative',
density: 1400,
erosionRate: 0.020, // inch/s @ 300 psi
pressureExponent: 0.35, // power-law exponent
tempLimit: 2100, // K
notes: 'Best for chamber walls'
})
For structural materials:
STRUCTURAL_MATERIALS.push({
id: 'newMaterial',
name: 'New Material',
density: 4500, // kg/m³
yieldStrength: 900, // MPa
youngModulus: 200, // GPa
meltingPoint: 1800, // K
cte: 12e-6, // 1/K
notes: 'Good for hot sections'
})
Changes auto-appear in UI (hot reload).
Adding a New Page
- Create page component (
src/pages/NewPage.jsx):
import { useState } from 'react'
export default function NewPage() {
return (
<div className="flex-1 overflow-auto p-6">
<h1 className="text-3xl font-bold mb-4">New Feature</h1>
{/* Your content */}
</div>
)
}
- Add route in
src/App.jsx:
import NewPage from './pages/NewPage.jsx'
// In Routes:
<Route path="/new-feature" element={<NewPage />} />
- Add navigation link in header if needed:
<NavLink
to="/new-feature"
className={/* classes */}
>
New Feature
</NavLink>
Adding a 3D Component
File: src/components/new3d/NewComponent.jsx
Use React Three Fiber (@react-three/fiber):
import { Canvas } from '@react-three/fiber'
import { OrbitControls } from '@react-three/drei'
export default function NewComponent() {
return (
<Canvas>
<ambientLight intensity={0.6} />
<directionalLight position={[5, 5, 5]} />
<mesh>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="red" />
</mesh>
<OrbitControls />
</Canvas>
)
}
Code Style & Conventions
Naming
- Components: PascalCase (
VariableCard.jsx) - Files: match component name or use kebab-case for utilities
- Variables/functions: camelCase
- Constants: UPPER_SNAKE_CASE
- React hooks:
useFeatureName
File Organization
// 1. Imports (grouped)
import React, { useState, useMemo } from 'react'
import { helper } from '../utils'
import './styles.css'
// 2. Types/Constants
const DEFAULT_VALUE = 10
// 3. Component
export default function MyComponent({ prop1, prop2 }) {
// Hooks
const [state, setState] = useState(0)
// Computed values
const memoized = useMemo(() => {
return expensiveCalculation()
}, [deps])
// Event handlers
const handleClick = () => { /* ... */ }
// JSX
return <div>...</div>
}
Styling
- Use Tailwind CSS classes
- Keep dark theme (slate-950 background)
- Responsive: mobile-first, use Tailwind breakpoints
<div className="px-4 py-2 rounded-md bg-slate-800 hover:bg-slate-700 transition-colors">
Click me
</div>
Comments
- Add only for why, not what
- Keep code self-documenting
- Avoid comments for obvious logic
// ✅ Good: explains intent
// Pressure correction for temperature variation
const correctedRate = baseRate * Math.pow(temp / refTemp, exponent)
// ❌ Bad: obvious from code
const x = x + 1 // increment x
Testing
Currently no test suite. Recommended additions:
Unit Tests (Jest)
// src/engine/__tests__/solver.test.js
import { solve } from '../solver'
test('solves linear equation', () => {
const eqs = [{
id: 'eq1',
variables: ['x', 'y'],
solvers: {
x: (inputs) => inputs.y * 2
}
}]
const result = solve(eqs, { y: 5 })
expect(result.x).toBe(10)
})
Component Tests (React Testing Library)
// src/components/__tests__/VariableCard.test.jsx
import { render, screen, fireEvent } from '@testing-library/react'
import VariableCard from '../VariableCard'
test('renders input field', () => {
render(<VariableCard variable={{name: 'thrust'}} />)
expect(screen.getByDisplayValue('0')).toBeInTheDocument()
})
test('updates on input change', () => {
const { getByDisplayValue } = render(<VariableCard />)
fireEvent.change(getByDisplayValue('0'), {target: {value: '100'}})
expect(getByDisplayValue('100')).toBeInTheDocument()
})
Manual Testing Checklist
- Solver computes correct values
- Engine design produces reasonable thrust/Isp
- Rocket design mass budget is sensible
- Trajectory plot shows reasonable path
- 3D models render without errors
- UI is responsive on mobile
Performance Considerations
Memoization
Use useMemo() for expensive calculations:
const results = useMemo(() => {
return engineDesignCalcs.calcCombustion(inputs) // expensive
}, [inputs])
Avoid Unnecessary Re-renders
- Lift state up only when needed
- Use
useCallback()for event handlers passed to children - Consider
React.memo()for pure components
const VariableCard = React.memo(({ variable, onChange }) => {
// Component only re-renders if variable or onChange changes
return <input onChange={onChange} />
})
Canvas Performance
- Limit trajectory states to ~5000 points
- Use fixed timestep (don't go below 0.01 s)
- Reduce Three.js geometry complexity if needed
Git Workflow
Branching
main— production-ready codefeature/*— new featuresfix/*— bug fixesdocs/*— documentation updates
Commits
Keep commits atomic and well-described:
git commit -m "Add pressure-corrected ablation model
- Implement power-law pressure exponent for erosion rate
- Add pressure factor display in Engine page
- Update knowledgebase with exponent values
- Update docs with pressure correction formula"
Pull Requests
Include:
- What changed and why
- How to test
- Any breaking changes
- Related issues
Debugging Tips
Browser DevTools
Console:
// Log solver state
console.log('Constraints:', solverState.constraints)
console.log('Results:', solverState.results)
React DevTools Extension:
- Inspect component props/state
- Profile render performance
- Trace updates
Hot Reload
Changes to src/ automatically reload (Vite).
- Engine modules reload: refresh browser
- Components/pages: usually hot reload instantly
Performance Profiling
// In React component
console.time('calculation')
const result = expensiveFunction()
console.timeEnd('calculation') // logs elapsed time
Common Pitfalls
❌ Don't: Mutate state directly
state.value = 100 // Wrong!
setState({...state, value: 100}) // Correct
❌ Don't: Create objects in dependencies
const obj = {x: 1}
useMemo(() => calc(obj), [obj]) // Runs every render!
✅ Do: Memoize dependencies
const obj = useMemo(() => ({x: 1}), [])
useMemo(() => calc(obj), [obj]) // Stable
❌ Don't: Forget dependencies
useEffect(() => {
doSomething(dep)
}, []) // Missing 'dep'!
✅ Do: Include all dependencies
useEffect(() => {
doSomething(dep)
}, [dep])
Asking for Help
Before asking:
- Check existing documentation
- Search similar equations/features
- Review console for errors
- Try simplifying the problem
When asking:
- Include error message
- Describe what you tried
- Provide minimal reproducible example
Roadmap Ideas
Potential future features:
- Multi-stage rockets — sequential stage design
- Thrust vector control — pitch/yaw steering
- Grid fin aerodynamics — drag & control surfaces
- Solid rocket motors — grain geometry, regression
- Regenerative cooling — thermal analysis
- Uncertainty analysis — Monte Carlo simulations
- Optimization — automated mass/performance optimization
- Data export — CAD, simulation formats (GMAT, Basilisk)
- Multiplayer — collaborative design sessions
Resources
Last Updated: 2025-02 | Status: Current