Skip to content

Commit

Permalink
Merge pull request #5 from source-academy/dev
Browse files Browse the repository at this point in the history
[0.0.4] Update Storybook and add Transformations
  • Loading branch information
YeluriKetan authored Apr 8, 2024
2 parents 6290d1e + d45c722 commit 470a1b9
Show file tree
Hide file tree
Showing 153 changed files with 2,983 additions and 979 deletions.
2 changes: 1 addition & 1 deletion .storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const preview: Preview = {
},
}, options: {
storySort: {
order: ['Getting started', ['nbody', 'Installation', 'Integration'], 'Usage', 'Examples'],
order: ['nbody', 'Installation', 'Quick Start', 'Integration', 'Contribute', 'Define', ['Intro', 'Force', 'Simulate Function', 'Transformation'], 'Visualize', ['Intro', 'Dimension', 'Multiverse', 'Record', 'Controller', 'Trails', 'Debug Info'], 'Showcase', ['Intro', 'Analemma', ['Intro', 'Sun-Earth'],'HorseshoeOrbit', ['Intro', '54509 YORP'], 'SolarSystem']],
},
}
},
Expand Down
85 changes: 85 additions & 0 deletions .storybook/stories/Contribute.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Meta, Story } from "@storybook/blocks";

<Meta title="Contribute" />

# Contribute

**nbody** is maintained by the open-source [Source Academy](https://github.com/source-academy/) development community based in National University of Singapore. Anyone from anywhere is free and welcome to contribute to our project and our community.

- [Developer Guide](#developer-guide)
- [Clone the repository](#clone-the-repository)
- [Install packages](#install-packages)
- [Developer Setup](#developer-setup)
- [Directory Structure](#directory-structure)
- [CLI Commands](#cli-commands)
- [Roadmap](#roadmap)
## Developer Guide

Here's a quick guide to get started on contributing to _nbody_.

### Clone the repository

```bash
git clone https://github.com/source-academy/nbody
```

### Install packages

We use [Yarn 1](https://classic.yarnpkg.com/lang/en/) as the package manager for its speed and developer experience. This installs all depenedencies required for the development of the project.

```bash
cd nbody
yarn install
```

### Developer Setup

Core libraries used in the project
- [three](https://threejs.org/) - For 3D rendering
- [plotly.js](https://plotly.com/javascript/) - For 2D rendering
- [lil-gui](https://lil-gui.georgealways.com/) - For GUI controls

Development setup of the project

- Source code is written in ES6 format in [Typescript](https://www.typescriptlang.org/)
- [ESLint](https://eslint.org/) for linting and ensuring code quality. If developing in VSCode, install the ESLint extension for real-time linting and format on save.
- Code comments in JSdoc format, API documentation generated using [Typedoc](https://typedoc.org/)
- [Storybook](https://storybook.js.org/) for general documentation and demo showcase
- [React](https://react.dev/) and [Vite](https://vitejs.dev/) template for component development

### Directory Structure

- `.storybook`
- Contains configuration and components for Storybook documentation
- `dist`
- Contains transpiled code along with type definitions
- `docs`
- Built documentation
- `api`
- Built API documentation
- `scripts`
- Contains miscellaneous scripts for development like clean
- `src`
- Contains source code
- package.json
- tsconfig.json



### CLI Commands

- **clean** - Clean `dist` (build) and `docs` folders
- **lint** - Run ESLint on `src` directory to ensure source code adheres to coding standards
- **docs** - Generate documentation - both Storybook and API reference
- **docs:api** - Generate only API documentation
- **docs:storybook** - Generate only Storybook documentation
- **storybook** - Preview Storybook components
- **build** - Build the project - lints, cleans, transpiles and generates documentation. Used for deployment.
- **dev** - Simple build and linking for local development
- **test** - Run tests

## Roadmap

Contributors are free to pick up any of the following tasks or suggest new features.

- Setup a bundled version of the project for CDN deployment
132 changes: 132 additions & 0 deletions .storybook/stories/Define/Force.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { Meta, Story } from '@storybook/blocks';

<Meta title="Define/Force" />

# Force

A force object encapsulates logic for calculating forces acting on celestial bodies due to other objects or environment. It has a getForces method that takes in an array of celestial bodies and returns an array of forces acting on each body. It is defined as the following Typescript interface.

```typescript
interface Force {
getForces(bodies: CelestialBody[]): Vector3[];
}
```

Full API reference can be found [here](https://source-academy.github.io/nbody/api/interfaces/Force.html).

- [Inbuilt Forces](#inbuilt-forces)
- [Javascript](#javascript)
- [Typescript](#typescript)

## Inbuilt Forces

### Gravity

Create a [newtonian gravitational](https://en.wikipedia.org/wiki/Newton%27s_law_of_universal_gravitation) force object with a gravitational constant of `6.674e-11`. You can also pass in a custom gravitational constant.

```javascript
new Gravity();
new Gravity(10);
```

### Centripetal Force

Create a [centripetal](https://en.wikipedia.org/wiki/Centripetal_force) force object with a center of rotation. Default center is `(0, 0, 0)`.

```javascript
new CentripetalForce();
new CentripetalForce(new Vector3(x, y, z));
```

### Combined Force

Create a force object that is a result of additively combining multiple forces acting on a system of bodies.

```javascript
new CombinedForce([new Gravity(), new CentripetalForce()]);
```

### Lambda Force

Create a force object that uses a [lambda/arrow](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) function to calculate forces.

```javascript
new LambdaForce((bodies) => {
return bodies.map(body => new Vector3(0, 0, 0)); // zero force
});
```

## Javascript

You can define and configure your own force object in javascript with a getForces method as follows

```javascript
// gravitational constant
const G = 6.67430e-11;

// define your own newtonian gravitational force
const gravity = {
// must contain a getForces method
getForces(bodies) {
let n = bodies.length;
let ans = [];
for (let i = 0; i < n; i++) {
ans.push(new Vector3(0, 0, 0));
}
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
let currForce = this.calcNewtonian(bodies[i], bodies[j]);
ans[i].add(currForce);
ans[j].sub(currForce);
}
}
return ans;
},
// helper function to calculate force between two bodies
calcNewtonian(a, b) {
let distSq = a.position.distanceToSquared(b.position);
let forceVal = (G * a.mass * b.mass) / distSq;
return b.position
.clone()
.sub(a.position)
.normalize()
.multiplyScalar(forceVal);
}
}
```

## Typescript

You can define and configure your own force object in typescript by implementing the Force interface as follows

```typescript
class Gravity implements Force {
readonly G: number = 6.674e-11;

getForces(bodies: CelestialBody[]): Vector3[] {
let n = bodies.length;
let ans: Vector3[] = [];
for (let i = 0; i < n; i++) {
ans.push(new Vector3(0, 0, 0));
}
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
let currForce = this.calcNewtonian(bodies[i], bodies[j]);
ans[i].add(currForce);
ans[j].sub(currForce);
}
}
return ans;
}

private calcNewtonian(a: CelestialBody, b: CelestialBody): Vector3 {
let distSq = a.position.distanceToSquared(b.position);
let forceVal = (this.G * a.mass * b.mass) / distSq;
return b.position
.clone()
.sub(a.position)
.normalize()
.multiplyScalar(forceVal);
}
}
```
11 changes: 11 additions & 0 deletions .storybook/stories/Define/Intro.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Meta, Story } from '@storybook/blocks';

<Meta title="Define/Intro" />

# Define

The library provides a set of predefined building blocks (force, simulate function, transformation) that can be used to define a simulation. However, the library is designed to be extensible and allows users to define their own building blocks in either JavaScript or TypeScript. Check out each building block's page for more details.

- [Force](?path=/docs/define-force--docs)
- [Simulate Function](?path=/docs/define-simulate-function--docs)
- [Transformation](?path=/docs/define-transformation--docs)
122 changes: 122 additions & 0 deletions .storybook/stories/Define/SimulateFunction.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { Meta, Story } from '@storybook/blocks';

<Meta title="Define/Simulate Function" />

# Simulate Function

A **Simulate Function** object encapsulates logic for advancing the state of the universe over time, usually using [numerical integration](https://en.wikipedia.org/wiki/Numerical_integration). It has a simulate method that takes in a time step, current state of the universe, (optionally the previous state of the universe) and returns the next state of the universe.

```typescript
interface SimulateFunction {
simulate(deltaT: number, currState: State, prevState: State): State
}
```

Full API reference can be found [here](https://source-academy.github.io/nbody/api/interfaces/SimulateFunction.html).

- [Inbuilt Simulate Functions](#inbuilt-simulate-functions)
- [Javascript](#javascript)
- [Typescript](#typescript)

## Inbuilt Simulate Functions

### Velocity Verlet

Create a [velocity verlet](https://en.wikipedia.org/wiki/Verlet_integration#Velocity_Verlet) integrator. Uses newtonian gravity by default, or the provided force object.

```javascript
new VelocityVerletSim();
new VelocityVerletSim(customForce);
```

### Explicit Euler

Create a [explicit euler](https://en.wikipedia.org/wiki/Explicit_and_implicit_methods) integrator. Uses newtonian gravity by default, or the provided force object.

```javascript
new ExplicitEulerSim();
new ExplicitEulerSim(customForce);
```

### Semi Implicit Euler

Create a [semi-implicit euler](https://en.wikipedia.org/wiki/Explicit_and_implicit_methods) integrator. Uses newtonian gravity by default, or the provided force object.

```javascript
new SemiImplicitEulerSim();
new SemiImplicitEulerSim(customForce);
```

### Runge-Kutta Order 4

Create a [runge-kutta order 4](https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods) integrator. Uses newtonian gravity by default, or the provided force object. Optionally, you can provide the weight coefficients for the averaging step.

```javascript
new RungeKutta4Sim();
new RungeKutta4Sim(customForce);
new RungeKutta4Sim(customForce, [1, 2, 2, 1]);
```

### Lambda integrator

Create a simulate function from a lambda function.

```javascript
new LambdaSim((deltaT, currState, prevState) => {
// your logic here
});
```

## Javascript

You can define and configure your own simulate function object in javascript with a simulate method as follows

```javascript
const explicitEulerSim = {
simulate(deltaT, currState, prevState) {
const updatedBodies = currState.bodies.map((b) =>
b.clone(
this.rateUpdate(b.position, b.velocity, deltaT),
this.rateUpdate(b.velocity, b.acceleration, deltaT)
)
);
const updatedForces = customForce.getForces(updatedBodies);
updatedBodies.forEach((b, i) => {
b.acceleration = updatedForces[i].divideScalar(b.mass);
});
return new State(updatedBodies);
},
rateUpdate(prev, rate, deltaT) {
return rate.clone().multiplyScalar(deltaT).add(prev);
},
};
```

## Typescript

You can define and configure your own simulate function object in Typescript by implementing the SimulateFunction interface as follows

```typescript
class ExplicitEulerSim implements SimulateFunction {
force: Force = new Gravity();

simulate(deltaT: number, currState: State): State {
const updatedBodies = currState.bodies.map((b) => b.clone(
this.rateUpdate(b.position, b.velocity, deltaT),
this.rateUpdate(b.velocity, b.acceleration, deltaT),
));
const updatedForces = this.force.getForces(updatedBodies);
updatedBodies.forEach((b, i) => {
b.acceleration = updatedForces[i].divideScalar(b.mass);
});
return new State(updatedBodies);
}

private rateUpdate(prev: Vector3, rate: Vector3, deltaT: number) {
return rate.clone()
.multiplyScalar(deltaT)
.add(prev);
}
}

```
Loading

0 comments on commit 470a1b9

Please sign in to comment.