497 lines
10 KiB
Markdown
497 lines
10 KiB
Markdown
# Contributing Guide
|
|
|
|
## Getting Started
|
|
|
|
### Prerequisites
|
|
- Node.js 18+
|
|
- npm or yarn
|
|
- Basic understanding of React, JavaScript
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
# 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](ARCHITECTURE.md) for detailed breakdown.
|
|
|
|
---
|
|
|
|
## Common Tasks
|
|
|
|
### Adding a New Equation
|
|
|
|
**File**: `src/engine/equations.js`
|
|
|
|
1. **Check if variables exist** in `variables.js`
|
|
- If not, add them with `id`, `name`, `unit`, `category`, `type`
|
|
|
|
2. **Add equation object**:
|
|
|
|
```javascript
|
|
{
|
|
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
|
|
}
|
|
}
|
|
```
|
|
|
|
3. **Test manually**
|
|
- Go to Solver page
|
|
- Drag variables onto workspace
|
|
- Set constraints, verify solver computes correct values
|
|
|
|
### For Transcendental Equations (Bisection)
|
|
|
|
```javascript
|
|
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:**
|
|
```javascript
|
|
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:**
|
|
```javascript
|
|
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:**
|
|
```javascript
|
|
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
|
|
|
|
1. **Create page component** (`src/pages/NewPage.jsx`):
|
|
|
|
```javascript
|
|
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>
|
|
)
|
|
}
|
|
```
|
|
|
|
2. **Add route** in `src/App.jsx`:
|
|
|
|
```javascript
|
|
import NewPage from './pages/NewPage.jsx'
|
|
|
|
// In Routes:
|
|
<Route path="/new-feature" element={<NewPage />} />
|
|
```
|
|
|
|
3. **Add navigation link** in header if needed:
|
|
|
|
```javascript
|
|
<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`):
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
<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
|
|
|
|
```javascript
|
|
// ✅ 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)
|
|
|
|
```javascript
|
|
// 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)
|
|
|
|
```javascript
|
|
// 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:
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
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 code
|
|
- `feature/*` — new features
|
|
- `fix/*` — bug fixes
|
|
- `docs/*` — documentation updates
|
|
|
|
### Commits
|
|
|
|
Keep commits atomic and well-described:
|
|
|
|
```bash
|
|
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:**
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
// In React component
|
|
console.time('calculation')
|
|
const result = expensiveFunction()
|
|
console.timeEnd('calculation') // logs elapsed time
|
|
```
|
|
|
|
---
|
|
|
|
## Common Pitfalls
|
|
|
|
### ❌ Don't: Mutate state directly
|
|
```javascript
|
|
state.value = 100 // Wrong!
|
|
setState({...state, value: 100}) // Correct
|
|
```
|
|
|
|
### ❌ Don't: Create objects in dependencies
|
|
```javascript
|
|
const obj = {x: 1}
|
|
useMemo(() => calc(obj), [obj]) // Runs every render!
|
|
```
|
|
|
|
### ✅ Do: Memoize dependencies
|
|
```javascript
|
|
const obj = useMemo(() => ({x: 1}), [])
|
|
useMemo(() => calc(obj), [obj]) // Stable
|
|
```
|
|
|
|
### ❌ Don't: Forget dependencies
|
|
```javascript
|
|
useEffect(() => {
|
|
doSomething(dep)
|
|
}, []) // Missing 'dep'!
|
|
```
|
|
|
|
### ✅ Do: Include all dependencies
|
|
```javascript
|
|
useEffect(() => {
|
|
doSomething(dep)
|
|
}, [dep])
|
|
```
|
|
|
|
---
|
|
|
|
## Asking for Help
|
|
|
|
**Before asking:**
|
|
1. Check existing documentation
|
|
2. Search similar equations/features
|
|
3. Review console for errors
|
|
4. 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
|
|
|
|
- [React Docs](https://react.dev)
|
|
- [Tailwind CSS](https://tailwindcss.com)
|
|
- [Three.js Docs](https://threejs.org/docs)
|
|
- [React Three Fiber](https://docs.pmnd.rs/react-three-fiber)
|
|
- [Vite Docs](https://vitejs.dev)
|
|
|
|
---
|
|
|
|
**Last Updated**: 2025-02 | **Status**: Current
|