diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 9db75e4..e334e6b 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -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']], }, } }, diff --git a/.storybook/stories/Contribute.mdx b/.storybook/stories/Contribute.mdx new file mode 100644 index 0000000..a00ac21 --- /dev/null +++ b/.storybook/stories/Contribute.mdx @@ -0,0 +1,85 @@ +import { Meta, Story } from "@storybook/blocks"; + + + +# 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 diff --git a/.storybook/stories/Define/Force.mdx b/.storybook/stories/Define/Force.mdx new file mode 100644 index 0000000..3112369 --- /dev/null +++ b/.storybook/stories/Define/Force.mdx @@ -0,0 +1,132 @@ +import { Meta, Story } from '@storybook/blocks'; + + + +# 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); + } +} +``` \ No newline at end of file diff --git a/.storybook/stories/Define/Intro.mdx b/.storybook/stories/Define/Intro.mdx new file mode 100644 index 0000000..71569c7 --- /dev/null +++ b/.storybook/stories/Define/Intro.mdx @@ -0,0 +1,11 @@ +import { Meta, Story } from '@storybook/blocks'; + + + +# 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) diff --git a/.storybook/stories/Define/SimulateFunction.mdx b/.storybook/stories/Define/SimulateFunction.mdx new file mode 100644 index 0000000..98bef1b --- /dev/null +++ b/.storybook/stories/Define/SimulateFunction.mdx @@ -0,0 +1,122 @@ +import { Meta, Story } from '@storybook/blocks'; + + + +# 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); + } +} + +``` \ No newline at end of file diff --git a/.storybook/stories/Define/Transformation.mdx b/.storybook/stories/Define/Transformation.mdx new file mode 100644 index 0000000..932d3a2 --- /dev/null +++ b/.storybook/stories/Define/Transformation.mdx @@ -0,0 +1,72 @@ +import { Meta, Story } from '@storybook/blocks'; + + + +# Transformation + +TODO + +```typescript +export interface Transformation { + transform(state: State, deltaT: number): State; +} +``` + +Full API reference can be found [here](https://source-academy.github.io/nbody/api/interfaces/Transformation.html). + +- [Inbuilt Transformations](#inbuilt-transformations) +- [Javascript](#javascript) +- [Typescript](#typescript) + +## Inbuilt Transformations + +### Body Center Transformation + +TODO + +```javascript +new BodyCenterTransformation(); +``` + +### Center of Mass Transformation + +TODO + +```javascript +new CoMTransformation(); +``` + +### Rotate Transformation + +TODO + +```javascript +new RotateTransformation(new Vector3(0, 1, 0), Math.PI / 2); +``` + +### Lambda Transformation + +TODO + +```javascript +new LambdaTransformation((state, deltaT) => { + // your transformation logic here + return state; +}); +``` + +## Javascript + +You can define and configure your own transformation object in javascript with a transform method as follows + +```javascript +TODO +``` + +## Typescript + +You can define and configure your own transformation object in typescript by implementing the Transformation interface as follows + +```typescript +TODO +``` \ No newline at end of file diff --git a/.storybook/stories/Examples/Simulation.mdx b/.storybook/stories/Examples/Simulation.mdx deleted file mode 100644 index e9c77a7..0000000 --- a/.storybook/stories/Examples/Simulation.mdx +++ /dev/null @@ -1,18 +0,0 @@ -import { Meta, Story } from '@storybook/blocks'; - -import * as SimulationStories from './Simulation.stories'; - - - -# Button - -Button is a clickable interactive element that triggers a response. - -You can place text and icons inside of a button. - -Buttons are often used for form submissions and to toggle elements into view. - -## Usage - - - \ No newline at end of file diff --git a/.storybook/stories/Examples/Simulation.stories.tsx b/.storybook/stories/Examples/Simulation.stories.tsx deleted file mode 100644 index 44a9b5a..0000000 --- a/.storybook/stories/Examples/Simulation.stories.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { Simulation } from './Simulation'; - -// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export -const meta = { - title: 'Examples/Simulation', - component: Simulation, - parameters: { - // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout - layout: 'centered', - controls: { - disable: true, - } - }, - // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs - tags: [], - // More on argTypes: https://storybook.js.org/docs/api/argtypes - argTypes: { - // storyName: { - - // } - // visType: { - - // } - // record: { - - // } - // looped: { - - // } - // controller: { - - // } - // showTrails: { - - // } - // showDebugInfo: { - - // } - // maxFrameRate: { - - // } - // maxTrailLength: { - - // } - }, - // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args - args: { }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args -export const TwoDim: Story = { - args: { - storyName: '2D', - }, -}; - -export const ThreeDim: Story = { - args: { - storyName: '3D', - visType: '3D', - }, -}; \ No newline at end of file diff --git a/.storybook/stories/Examples/Simulation.tsx b/.storybook/stories/Examples/Simulation.tsx deleted file mode 100644 index 0df1757..0000000 --- a/.storybook/stories/Examples/Simulation.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; -import { CelestialBody, ControllerType, Gravity, Simulation as NbodySimulation, RungeKutta4Sim, State, Universe, Vector3, VisType } from "../../../src/index"; - -interface SimulationProps { - storyName: string; - visType?: VisType; - record?: boolean; - looped?: boolean; - controller?: ControllerType; - showTrails?: boolean; - showDebugInfo?: boolean; - maxFrameRate?: number; - maxTrailLength?: number; -} - -/** - * Primary UI component for user interaction - */ -export const Simulation = ({ - storyName = 'default', - visType = '2D', - record = false, - looped = true, - controller = 'ui', - showTrails = false, - showDebugInfo = true, - maxFrameRate = -1, - maxTrailLength = 100, - ...props -}: SimulationProps) => { - const divId = 'demo-' + storyName; - const force = new Gravity(1); - const a = new CelestialBody( - "a", - 1, - new Vector3(-0.97000436, 0.24308753, 0), - new Vector3(0.466203685, 0.43236573, 0), - new Vector3(0, 0, 0) - ); - const b = new CelestialBody( - "b", - 1, - new Vector3(0.97000436, -0.24308753, 0), - new Vector3(0.466203685, 0.43236573, 0), - new Vector3(0, 0, 0) - ); - const c = new CelestialBody( - "c", - 1, - new Vector3(0, 0, 0), - new Vector3(-2 * 0.466203685, -2 * 0.43236573, 0), - new Vector3(0, 0, 0) - ); - const universe: Universe = new Universe({ - label: "a", - currState: new State([a, b, c]), - color: "rgba(254, 209, 106, 1)", - simFunc: new RungeKutta4Sim(force, [1, 2, 2, 1]), - } - ); - - const simulation = new NbodySimulation([universe], { - visType, - record, - looped, - controller, - showTrails, - showDebugInfo, - maxFrameRate, - maxTrailLength, - }); - - return ( -
simulation.start(divId, 600, 600)}> -
- ); -}; diff --git a/.storybook/stories/Install.mdx b/.storybook/stories/Install.mdx index 5d42744..e20af3f 100644 --- a/.storybook/stories/Install.mdx +++ b/.storybook/stories/Install.mdx @@ -1,8 +1,10 @@ import { Meta, Story } from '@storybook/blocks'; - + -# Installation guide +# Installation + +## Via npm registry nbody is available on the npm registry as [nbody](https://www.npmjs.com/package/nbody). Simply follow the following install commands based on your package manager. Since nbody relies on [three](https://threejs.org/) and [Plotly.js](https://plotly.com/javascript/) for visualization of the simulations, they have to be installed alongside nbody. If you are using Typescript, you may also have to install type definitions as well. @@ -18,3 +20,14 @@ or yarn add nbody three plotly.js-dist yarn add -D @types/three @types/plotly.js ``` + +## Via CDN + +> **WARNING**: Using nbody via the CDN may not work as intended as peer dependencies of three and plotly.js would not be accessible to nbody during runtime. A bundled version is in the roadmap for future development - consider [contributing](?path=/docs/getting-started-contribute--docs). + +nbody is available via the unpkg CDN as follows. + +```html + +``` + diff --git a/.storybook/stories/Integration.mdx b/.storybook/stories/Integration.mdx index 49882e8..0b96d3c 100644 --- a/.storybook/stories/Integration.mdx +++ b/.storybook/stories/Integration.mdx @@ -1,20 +1,22 @@ -import { Meta, Story } from '@storybook/blocks'; +import { Meta, Story } from "@storybook/blocks"; - + # Integration Once installed into your `node_modules`, you can use **nbody** in your choice of your frontend framework just like you would any other client side library. ## Table of Contents + - [Compatible Frameworks](#compatible-frameworks) - [React](#react) - [Typescript](#typescript) ## Compatible Frameworks -Following combinations of Frontend frameworks + build tools + dev tools have been tested to be compatible with nbody. Consider raising a [request](https://github.com/source-academy/nbody/issues) if any frameworks are missing. Additionally do consider contributing to the project directly. +Following combinations of Frontend frameworks + build tools + dev tools have been tested to be compatible with nbody. Consider raising a [request](https://github.com/source-academy/nbody/issues) if any frameworks are missing. Additionally do consider [contributing](?path=/docs/contribute--docs) to the project directly. +- Plain HTML + JS - React + Vite - React + Vite + TS - React + Webpack @@ -22,60 +24,12 @@ Following combinations of Frontend frameworks + build tools + dev tools have bee ## React -```javascript -import { - CelestialBody, Gravity, VelocityVerletSim, State, Universe, Simulation, RungeKutta4Sim, ExplicitEulerSim, SemiImplicitEulerSim, CoMTransformation, Vector3 -} from "nbody"; - -function run(divId, width, height) { - let g = 1; - - let force = new Gravity(g); - let sim = new VelocityVerletSim(force); - - let a = new CelestialBody( - "a", - 1, - new Vector3(-0.97000436, 0.24308753, 0), - new Vector3(0.466203685, 0.43236573, 0), - new Vector3(0, 0, 0) - ); - - let b = new CelestialBody( - "b", - 1, - new Vector3(0.97000436, -0.24308753, 0), - new Vector3(0.466203685, 0.43236573, 0), - new Vector3(0, 0, 0) - ); +### Using ref - let c = new CelestialBody( - "c", - 1, - new Vector3(0, 0, 0), - new Vector3(-2 * 0.466203685, -2 * 0.43236573, 0), - new Vector3(0, 0, 0) - ); - - let state = new State([a, b, c]); - - let universe = new Universe({ - label: "1", - currState: state.clone(), - color: "rgba(112, 185, 177, 1)", - simFunc: sim, - }); - - let simulation = new Simulation(universe, { - visType: "3D", - showTrails: true, - controller: "ui", - }); - - simulation.start(divId, 800, 800, width, height); -} +```javascript +function CustomComponent() { + let simulation = new Simulation(...); -function App() { return (
run("demo-canvas", 800, 800)} + ref={() => simulation.start("demo-canvas", 800, 800);} >
); } - -export default App; ``` -## Typescript - -```typescript -class TranslateZTransformation implements Transformation { - transform(state: State, deltaT: number): State { - const newState = state.clone(); - newState.bodies.forEach((body) => { - body.position.z += 1; - }); - return newState; - } -} +### Using useEffect (recommended) -function run(divId: string) { - ... +```javascript +function CustomComponent() { + useEffect(() => { + let simulation = new Simulation(...); + simulation.start("demo-canvas", 800, 800); + return () => simulation.stop(); // properly cleanup the simulation + }, []); - let universe: Universe = new Universe({ - label: "Translated Universe", - currState: new State([a, b, c]), - color: "rgba(254, 209, 106, 1)", - simFunc: new VelocityVerletSim(new Gravity(1)), - transformations: [new TranslateZTransformation()] - } + return ( +
); - - ... } ``` \ No newline at end of file diff --git a/.storybook/stories/Nbody.mdx b/.storybook/stories/Nbody.mdx index 6d9e04f..a714b99 100644 --- a/.storybook/stories/Nbody.mdx +++ b/.storybook/stories/Nbody.mdx @@ -1,27 +1,42 @@ import { Meta, Story } from "@storybook/blocks"; - - -[API](https://source-academy.github.io/nbody/api) + # nbody -A JS/TS library to configure, simulate and visualize nbody simulations in the browser. +Frontend library to **configure**, **simulate** and **visualize** nbody simulations, entirely on the client-side browser. # Background The [**n-body problem**](https://en.wikipedia.org/wiki/N-body_problem), a cornerstone of celestial mechanics, involves predicting the motions of multiple objects interacting **gravitationally**. While solvable analytically for [two-bodies](https://en.wikipedia.org/wiki/Two-body_problem), we rely on computer simulations using numerical integration methods for three or more. -This project aims to bridge the _space_ between accuracy, accessibility, performance and education. With this JS/TS library, you can +This library aims to bridge the _space_ between accuracy, accessibility, performance and education. With *nbody*, you can - **Effortlessly configure** your system of bodies. - **Simulate with flexibility** utilizing various accurate and/or performant numerical integration methods or even write you own. - **Visually explore** and immerse yourself with customizable 2D and 3D visualizations and intuitive UI controls +# Where to next? + +- [Install](?path=/docs/getting-started-installation--docs) nbody for your project +- Get [started quickly](?path=/docs/getting-started-quick-start--docs) +- Read the [API Reference](https://source-academy.github.io/nbody/api/) +- Check out [Showcase](?path=/docs/showcase--docs) for some cool examples +- [Contribute](?path=/docs/contribute--docs) to the project + # Development -**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 to contribute to the project and the community. +**nbody** started off as the author's final year project (FYP) in AY23/24 at the National University of Singapore, under the insightful guidance of [Prof. Martin Henz](https://www.comp.nus.edu.sg/~henz/). The project is set for a public beta release by the end of the FYP duration. + +**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. + +## Thank you + +Eternally grateful to [Prof. Martin Henz](https://www.comp.nus.edu.sg/~henz/) for kick starting the project, as well as the invaluable guidance and support throughout the project. This project would also not have existed without the inspiration and achievements of + +- [Martin Vezina](https://medium.com/@m_vezina) and his [jsOrrery](https://mgvez.github.io/jsorrery/) +- [Ian Webster](https://github.com/typpo) and his [spacekit](https://typpo.github.io/spacekit/docs/index.html) -# Contributors +## Contributors - [Join us](?path=/docs/getting-started-contribute--docs) - [Yeluri Ketan](https://yeluriketan.vercel.app/) - Creator and Maintainer diff --git a/.storybook/stories/QuickStart.mdx b/.storybook/stories/QuickStart.mdx new file mode 100644 index 0000000..94d5933 --- /dev/null +++ b/.storybook/stories/QuickStart.mdx @@ -0,0 +1,128 @@ +import { Meta, Story } from "@storybook/blocks"; + +import * as Stories from "./Visualize/Dimension/Dimension.stories"; + + + +# Quick Start + +A quick start guide to configuring, simulating and visualizing a simple nbody simulation. + +- [Creating vectors](#creating-vectors) +- [Creating bodies](#creating-bodies) +- [Define a force](#define-a-force) +- [Define a simulate function](#define-a-simulate-function) +- [Creating a state](#creating-a-state) +- [Creating an universe](#creating-an-universe) +- [Put it all together](#put-it-all-together) +- [Next Steps](#next-steps) + +## Creating vectors + +A `Vector3` is a 3D vector with `x`, `y`, and `z` components. It is used to represent kinematic properties such as _position_, _velocity_, and _acceleration_. + +Create a vector object using the `Vector3` class. Inherited from **three.js** Vector3, full documentation can be found [here](https://threejs.org/docs/#api/en/math/Vector3). + +```javascript +let testVector = new Vector3(1, 2, 3); +``` + +## Creating bodies + +A `CelestialBody` is a celestial object with a _mass_ and kinematic properties such as _position_, _velocity_, and _acceleration_. It also has a unique _label_ for identification purposes. + +Create celestial bodies using the `CelestialBody` class. Full API reference can be found [here](https://source-academy.github.io/nbody/api/classes/CelestialBody.html). + +```javascript +let body1 = new CelestialBody( + "Body 1", // label + 1, // mass + new Vector3(-0.97000436, 0.24308753, 0), // position + new Vector3(0.466203685, 0.43236573, 0), // velocity + new Vector3(0, 0, 0) // acceleration +); + +let body2 = new CelestialBody( + "Body 2", + 1, + new Vector3(0.97000436, -0.24308753, 0), + new Vector3(0.466203685, 0.43236573, 0), + new Vector3(0, 0, 0) +); + +let body3 = new CelestialBody( + "Body 3", + 1, + new Vector3(0, 0, 0), + new Vector3(-2 * 0.466203685, -2 * 0.43236573, 0), + new Vector3(0, 0, 0) +); +``` + +## Define a force + +A force object encapsulates logic for calculating forces acting on celestial bodies due to other objects or environment. + +Create a force object using the `Force` interface or use one of the predefined forces. Full API reference can be found [here](https://source-academy.github.io/nbody/api/interfaces/Force.html). + +```javascript +// define a gravitational force with a strength of 1 +const customForce = new Gravity(1); +``` + +## Define a simulate function + +A simulate function object encapsulates logic for advancing the state of the universe over time using numerical integration. + +Create a simulate function (or [numerical integrator](https://en.wikipedia.org/wiki/Numerical_integration)) object using the `SimulateFunction` interface or use one of the predefined simulate functions. Full API reference can be found [here](https://source-academy.github.io/nbody/api/interfaces/SimulateFunction.html). + +```javascript +// define an explicit Euler integrator with the custom force +const customSimFunc = new ExplicitEulerSim(customForce); +``` + +## Creating a state + +A State is a snapshot of the universe at a given time. It contains an array of celestial bodies. + +Create a state object using the `State` class. Full API reference can be found [here](https://source-academy.github.io/nbody/api/classes/State.html). + +```javascript +// create a State object with the array of celestial bodies +let customState = new State([body1, body2]); +``` + +## Creating an universe + +A Universe is a container for the current state and previous states of the universe and the simulation function. It also has a unique label for identification purposes. + +Create a universe object using the `Universe` class. Full API reference can be found [here](https://source-academy.github.io/nbody/api/classes/Universe.html). + +```javascript +let universe = new Universe({ + label: "Universe 1", + currState: customState, + simFunc: customSimFunc, +}); +``` + +## Put it all together + +A Simulation object controls the universe(s), simulation loop and rendering of the simulation. + +Create a simulation object using the `Simulation` class. Full API reference can be found [here](https://source-academy.github.io/nbody/api/classes/Simulation.html). + +```javascript +let simulation = new Simulation(universe, {}); +simulation.start(, 800, 800); +``` + +
+ +
+ +You've done it! You just simulated a special configuration of the [three-body](https://en.wikipedia.org/wiki/Three-body_problem) system using the _nbody_ library. Feel free to experiment with different configurations to see how systems evolve over time with various forces, integrators and transformations (read on to find out what those are). + +## Next Steps + +Checkout the [Configuration](?path=/docs/configuration-configuration--docs) section to learn how to configure a simulation with greater detail and customization. diff --git a/.storybook/stories/Showcase/Analemma/Analemma.mdx b/.storybook/stories/Showcase/Analemma/Analemma.mdx new file mode 100644 index 0000000..067f81c --- /dev/null +++ b/.storybook/stories/Showcase/Analemma/Analemma.mdx @@ -0,0 +1,5 @@ +import { Meta, Story } from '@storybook/blocks'; + + + +# Analemma \ No newline at end of file diff --git a/.storybook/stories/Showcase/Analemma/Analemma.stories.tsx b/.storybook/stories/Showcase/Analemma/Analemma.stories.tsx new file mode 100644 index 0000000..3a15c06 --- /dev/null +++ b/.storybook/stories/Showcase/Analemma/Analemma.stories.tsx @@ -0,0 +1,35 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Simulation } from "../../Simulation"; +import { fig8 } from "../../Universe"; +import { sunEarth } from "./Analemma"; + +const meta = { + title: "Showcase/Analemma", + component: Simulation, + parameters: { + layout: "centered", + controls: { + disable: true, + }, + }, + tags: [], + argTypes: {}, + args: {}, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const SunEarthAnalemma: Story = { + args: { + storyName: "SunEarthAnalemma", + universe: [sunEarth], + controller: 'ui', + showTrails: true, + speed: 5000000, + visType: '3D', + width: 800, + maxTrailLength: 300, + showDebugInfo: true, + }, +}; diff --git a/.storybook/stories/Showcase/Analemma/Analemma.ts b/.storybook/stories/Showcase/Analemma/Analemma.ts new file mode 100644 index 0000000..056dfc7 --- /dev/null +++ b/.storybook/stories/Showcase/Analemma/Analemma.ts @@ -0,0 +1,38 @@ +import { BodyCenterTransformation, CelestialBody, CoMTransformation, RotateTransformation, RungeKutta4Sim, State, TimedRotateTransformation, Universe, Vector3 } from "../../../../src"; + +const SUN = new CelestialBody( + "Sun", + 1.989e30, + new Vector3(0, 0, 0), + new Vector3(0, 0, 0), + new Vector3(0, 0, 0) + ); + +const EARTH = new CelestialBody( + "Earth", + 5.972e24, + new Vector3(152.1e9, 0, 0), + new Vector3(0, 0, -29290), + new Vector3(0, 0, 0) + ); + + +const axialTilt = new RotateTransformation( + new Vector3(0, 0, 1), + -(23.4 / 180) * Math.PI + ); +const alignTilt = new RotateTransformation(new Vector3(0, 1, 0), -(Math.PI / 2)); +const bodyTranslate = new BodyCenterTransformation(1); + +export const sunEarth: Universe = new Universe({ + label: "Sun-Earth System", + currState: alignTilt.transform( + axialTilt.transform(bodyTranslate.transform(new State([SUN.clone(), EARTH.clone()]))) + ), + color: ["#FDB813", "#287AB8"], + simFunc: new RungeKutta4Sim(), + transformations: [ + bodyTranslate, + new TimedRotateTransformation(new Vector3(0, 1, 0), 365.25 * 24 * 60 * 60), + ], + }); \ No newline at end of file diff --git a/.storybook/stories/Showcase/Analemma/Sun-Earth.mdx b/.storybook/stories/Showcase/Analemma/Sun-Earth.mdx new file mode 100644 index 0000000..3b429a9 --- /dev/null +++ b/.storybook/stories/Showcase/Analemma/Sun-Earth.mdx @@ -0,0 +1,9 @@ +import { Meta, Story } from '@storybook/blocks'; + +import * as Stories from "./Analemma.stories"; + + + +# Sun-Earth Analemma + + \ No newline at end of file diff --git a/.storybook/stories/Showcase/HorseshoeOrbit/54509 YORP.mdx b/.storybook/stories/Showcase/HorseshoeOrbit/54509 YORP.mdx new file mode 100644 index 0000000..b5841aa --- /dev/null +++ b/.storybook/stories/Showcase/HorseshoeOrbit/54509 YORP.mdx @@ -0,0 +1,13 @@ +import { Meta, Story } from '@storybook/blocks'; + +import * as Stories from "./HorseshoeOrbit.stories"; + + + +# 54509 YORP + +54509 YORP is an Earth co-orbital asteroid that follows a horse shoe orbit + + + + \ No newline at end of file diff --git a/.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.mdx b/.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.mdx new file mode 100644 index 0000000..c647c16 --- /dev/null +++ b/.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.mdx @@ -0,0 +1,11 @@ +import { Meta, Story } from '@storybook/blocks'; + +import * as Stories from "./HorseshoeOrbit.stories"; + + + + +# Horseshoe Orbits + +TODO + diff --git a/.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.stories.tsx b/.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.stories.tsx new file mode 100644 index 0000000..d1ded76 --- /dev/null +++ b/.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.stories.tsx @@ -0,0 +1,33 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Simulation } from "../../Simulation"; +import { horseshoe } from "./HorseshoeOrbit"; + +const meta = { + title: "Showcase/Horseshoe Orbit", + component: Simulation, + parameters: { + layout: "centered", + controls: { + disable: true, + }, + }, + tags: [], + argTypes: {}, + args: {}, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const YORP: Story = { + args: { + storyName: "Horseshoe Orbit YORP", + universe: [horseshoe.yorp], + showDebugInfo: true, + controller: 'ui', + visType: '3D', + width: 800, + speed: 10000000, + showTrails: true, + }, +}; diff --git a/.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.ts b/.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.ts new file mode 100644 index 0000000..1903168 --- /dev/null +++ b/.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.ts @@ -0,0 +1,54 @@ +import { BodyCenterTransformation, CelestialBody, CoMTransformation, PinTransformation, RotateTransformation, RungeKutta4Sim, State, TimedRotateTransformation, Universe, Vector3 } from "../../../../src"; + +const yorpState = new State([ + new CelestialBody( + "Sun", + 1988500e24, + new Vector3(0, 0, 0), + new Vector3(0, 0, 0), + new Vector3(0, 0, 0) + ), + new CelestialBody( + "Earth", + 5.97219e24, + new Vector3( + -2.48109932596539e10, + 1.449948612736719e11, + -8.215203670851886e6 + ), + new Vector3( + -2.984146365518679e4, + -5.126262286859617e3, + 1.184224839788195 + ), + new Vector3(0, 0, 0) + ), + new CelestialBody( + "YORP", + 1, + new Vector3( + 1.789598196203594e11, + 4.67757011067789e10, + 5.131735873924753e9 + ), + new Vector3( + -5.641374152889482e3, + 2.28178307950743e4, + -6.507224186314708e1 + ), + new Vector3(0, 0, 0) + )]); +const yorp = new Universe({ + label: "54509 YORP", + currState: new RotateTransformation(new Vector3(1, 0, 0), Math.PI / 2).transform(yorpState.clone()), + simFunc: new RungeKutta4Sim(), + color: ["#FDB813", "#287AB8", "#767676"], + transformations: [ + new CoMTransformation(), + new PinTransformation(new Vector3(1, 0, 0), 1), + ], +}); + +export const horseshoe = { + yorp: yorp, +}; \ No newline at end of file diff --git a/.storybook/stories/Showcase/Intro.mdx b/.storybook/stories/Showcase/Intro.mdx new file mode 100644 index 0000000..430e0a1 --- /dev/null +++ b/.storybook/stories/Showcase/Intro.mdx @@ -0,0 +1,8 @@ +import { Meta, Story } from '@storybook/blocks'; + + + + +## Horseshoe Orbits +## Solar system +## Analemma diff --git a/.storybook/stories/Showcase/SolarSystem/SolarSystem.mdx b/.storybook/stories/Showcase/SolarSystem/SolarSystem.mdx new file mode 100644 index 0000000..2b3d5ef --- /dev/null +++ b/.storybook/stories/Showcase/SolarSystem/SolarSystem.mdx @@ -0,0 +1,10 @@ +import { Meta, Story } from '@storybook/blocks'; + +import * as Stories from "./SolarSystem.stories"; + + + +## +```js +``` +
\ No newline at end of file diff --git a/.storybook/stories/Showcase/SolarSystem/SolarSystem.stories.tsx b/.storybook/stories/Showcase/SolarSystem/SolarSystem.stories.tsx new file mode 100644 index 0000000..d4ad2e7 --- /dev/null +++ b/.storybook/stories/Showcase/SolarSystem/SolarSystem.stories.tsx @@ -0,0 +1,27 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Simulation } from "../../Simulation"; +import { fig8 } from "../../Universe"; + +const meta = { + title: "Showcase/Solar System", + component: Simulation, + parameters: { + layout: "centered", + controls: { + disable: true, + }, + }, + tags: [], + argTypes: {}, + args: {}, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const SolarSystem: Story = { + args: { + storyName: "3D", + universe: [fig8], + }, +}; diff --git a/.storybook/stories/Simulation.tsx b/.storybook/stories/Simulation.tsx new file mode 100644 index 0000000..c5ee722 --- /dev/null +++ b/.storybook/stories/Simulation.tsx @@ -0,0 +1,75 @@ +import React, { useEffect } from 'react'; +import { ControllerType, Simulation as NbodySimulation, Universe, VisType } from "../../src/index"; + +interface SimulationProps { + storyName: string; + universe: Universe[]; + visType?: VisType; + record?: boolean; + looped?: boolean; + controller?: ControllerType; + showTrails?: boolean; + showDebugInfo?: boolean; + maxFrameRate?: number; + maxTrailLength?: number; + width?: number; + callback?: (simulation: NbodySimulation) => () => void; + speed?: number, + paused?: boolean , + recordFor?: number, +} + +/** + * Primary UI component for user interaction + */ +export const Simulation = ({ + storyName = 'default', + universe = [], + visType = '2D', + record = false, + looped = true, + controller = 'none', + showTrails = false, + showDebugInfo = false, + maxFrameRate = -1, + maxTrailLength = 100, + width = 400, + callback = (sim) => () => { }, + speed = 1, + paused = false, + recordFor = 5, + ...props +}: SimulationProps) => { + const divId = "demo-" + storyName; + const [simulation, setSimulation] = React.useState(null); + if (simulation === null) { + setSimulation(new NbodySimulation(universe.map(u => u.clone()), { + visType, + record, + looped, + controller, + showTrails, + showDebugInfo, + maxFrameRate, + maxTrailLength, + })); + } + + useEffect(() => { + simulation?.start(divId, width, width, speed, paused, recordFor); + let callbackClear = () => {}; + if (simulation) { + callbackClear = callback(simulation); + } + return () => { + callbackClear(); + simulation?.stop(); + }; + }, []); + return ( +
+
+ ); +}; \ No newline at end of file diff --git a/.storybook/stories/Universe.ts b/.storybook/stories/Universe.ts new file mode 100644 index 0000000..03fc6aa --- /dev/null +++ b/.storybook/stories/Universe.ts @@ -0,0 +1,72 @@ +import { + CelestialBody, + CoMTransformation, + Gravity, + PinTransformation, + RotateTransformation, + RungeKutta4Sim, + SemiImplicitEulerSim, + State, + Universe, + Vector3, +} from "../../src"; + +export const fig8 = new Universe({ + label: "Fig 8 Universe", + currState: new State([ + new CelestialBody( + "Body 1", + 1, + new Vector3(-0.97000436, 0.24308753, 0), + new Vector3(0.466203685, 0.43236573, 0), + new Vector3(0, 0, 0) + ), + new CelestialBody( + "Body 2", + 1, + new Vector3(0.97000436, -0.24308753, 0), + new Vector3(0.466203685, 0.43236573, 0), + new Vector3(0, 0, 0) + ), + new CelestialBody( + "Body 3", + 1, + new Vector3(0, 0, 0), + new Vector3(-2 * 0.466203685, -2 * 0.43236573, 0), + new Vector3(0, 0, 0) + ), + ]), + simFunc: new RungeKutta4Sim(new Gravity(1), [1, 2, 2, 1]), +}); + +export const multiFig8 = [ + fig8, + new Universe({ + label: "Fig 8 Universe 2", + currState: new State([ + new CelestialBody( + "Body 1", + 1, + new Vector3(-0.97000436, 0.24308753, 0), + new Vector3(0.466203685, 0.43236573, 0), + new Vector3(0, 0, 0) + ), + new CelestialBody( + "Body 2", + 1, + new Vector3(0.97000436, -0.24308753, 0), + new Vector3(0.466203685, 0.43236573, 0), + new Vector3(0, 0, 0) + ), + new CelestialBody( + "Body 3", + 1, + new Vector3(0, 0, 0), + new Vector3(-2 * 0.466203685, -2 * 0.43236573, 0), + new Vector3(0, 0, 0) + ), + ]), + simFunc: new SemiImplicitEulerSim(new Gravity(1)), + color: "red", + }), +]; diff --git a/.storybook/stories/Usage/CelestialBody.mdx b/.storybook/stories/Usage/CelestialBody.mdx deleted file mode 100644 index e0a997d..0000000 --- a/.storybook/stories/Usage/CelestialBody.mdx +++ /dev/null @@ -1,13 +0,0 @@ -import { Meta, Story } from '@storybook/blocks'; - - - -```ts - const a = new CelestialBody( - "a", - 1, - new Vector3(-0.97000436, 0.24308753, 0), - new Vector3(0.466203685, 0.43236573, 0), - new Vector3(0, 0, 0) - ); -``` \ No newline at end of file diff --git a/.storybook/stories/Visualize/Controller/Controller.mdx b/.storybook/stories/Visualize/Controller/Controller.mdx new file mode 100644 index 0000000..0cf1aff --- /dev/null +++ b/.storybook/stories/Visualize/Controller/Controller.mdx @@ -0,0 +1,29 @@ +import { Meta, Story } from '@storybook/blocks'; + +import * as ControllerStories from "./Controller.stories"; + + + +## None (default) +```js +new Simulation(universe, { + controller: 'none' +}) +``` +
+ +## Ui +```js +new Simulation(universe, { + controller: 'ui' +}) +``` +
+ +## Code +```js +new Simulation(universe, { + controller: 'code' +}) +``` +
\ No newline at end of file diff --git a/.storybook/stories/Visualize/Controller/Controller.stories.tsx b/.storybook/stories/Visualize/Controller/Controller.stories.tsx new file mode 100644 index 0000000..9b9bd39 --- /dev/null +++ b/.storybook/stories/Visualize/Controller/Controller.stories.tsx @@ -0,0 +1,54 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Simulation } from '../../Simulation'; +import { fig8 } from '../../Universe'; + +const meta = { + title: 'Visualize/Controller', + component: Simulation, + parameters: { + layout: 'centered', + controls: { + disable: true, + } + }, + tags: [], + argTypes: { + }, + args: { }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const None: Story = { + args: { + storyName: 'None', + universe: [fig8], + controller: 'none' + }, +}; + +export const Ui: Story = { + args: { + storyName: 'Ui', + universe: [fig8], + controller: 'ui', + }, +}; + +export const Code: Story = { + args: { + storyName: 'Code', + universe: [fig8], + controller: 'code', + callback: (sim) => { + const id = setInterval(() => { + sim.setShowUniverse(fig8.label, !sim.getShowUniverse(fig8.label)); + }, 500); + + return () => { + clearInterval(id); + } + } + }, +}; \ No newline at end of file diff --git a/.storybook/stories/Visualize/Debug Info/Debug.mdx b/.storybook/stories/Visualize/Debug Info/Debug.mdx new file mode 100644 index 0000000..32b1d17 --- /dev/null +++ b/.storybook/stories/Visualize/Debug Info/Debug.mdx @@ -0,0 +1,25 @@ +import { Meta, Story } from '@storybook/blocks'; + +import * as DebugStories from "./Debug.stories"; + + + +# Debug Info + +Passed as the `showDebugInfo` property in the Simulation config. This will display debug information in the corner of the simulation canvas. Click on the pop-up to toggle between frames showing the frame rate, time taken for each frame and the total memory usage. + +## Off (default) +```js +new Simulation(universe, { + showDebugInfo: false +}) +``` +
+ +## On +```js +new Simulation(universe, { + showDebugInfo: true +}) +``` +
\ No newline at end of file diff --git a/.storybook/stories/Visualize/Debug Info/Debug.stories.tsx b/.storybook/stories/Visualize/Debug Info/Debug.stories.tsx new file mode 100644 index 0000000..a94165e --- /dev/null +++ b/.storybook/stories/Visualize/Debug Info/Debug.stories.tsx @@ -0,0 +1,45 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Simulation } from '../../Simulation'; +import { fig8 } from '../../Universe'; + +const meta = { + title: 'Visualize/Debug Info', + component: Simulation, + parameters: { + layout: 'centered', + controls: { + disable: true, + } + }, + tags: [], + argTypes: { + }, + args: { }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const DebugInfoOn: Story = { + args: { + storyName: 'DebugInfoOn', + universe: [fig8], + showDebugInfo: true, + }, +}; + +export const DebugInfoOff: Story = { + args: { + storyName: 'DebugInfoOff', + universe: [fig8], + showDebugInfo: false, + }, +}; + +export const Ui: Story = { + args: { + storyName: 'Ui', + universe: [fig8], + controller: 'ui' + }, +}; \ No newline at end of file diff --git a/.storybook/stories/Visualize/Dimension/Dimension.mdx b/.storybook/stories/Visualize/Dimension/Dimension.mdx new file mode 100644 index 0000000..823a747 --- /dev/null +++ b/.storybook/stories/Visualize/Dimension/Dimension.mdx @@ -0,0 +1,25 @@ +import { Meta, Story } from '@storybook/blocks'; + +import * as DimensionStories from "./Dimension.stories"; + + + +# Dimension + +TODO + +## 2D (default) +```js +new Simulation(universe, { + visType: '2D', +}) +``` +
+ +## 3D +```js +new Simulation(universe, { + visType: '3D', +}) +``` +
\ No newline at end of file diff --git a/.storybook/stories/Visualize/Dimension/Dimension.stories.tsx b/.storybook/stories/Visualize/Dimension/Dimension.stories.tsx new file mode 100644 index 0000000..55aff9c --- /dev/null +++ b/.storybook/stories/Visualize/Dimension/Dimension.stories.tsx @@ -0,0 +1,36 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Simulation } from '../../Simulation'; +import { fig8 } from '../../Universe'; + +const meta = { + title: 'Visualize/Dimension', + component: Simulation, + parameters: { + layout: 'centered', + controls: { + disable: true, + } + }, + tags: [], + argTypes: { + }, + args: { }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const TwoD: Story = { + args: { + storyName: 'TwoD', + universe: [fig8], + }, +}; + +export const ThreeD: Story = { + args: { + storyName: 'ThreeD', + universe: [fig8], + visType: '3D', + }, +}; \ No newline at end of file diff --git a/.storybook/stories/Visualize/Intro.mdx b/.storybook/stories/Visualize/Intro.mdx new file mode 100644 index 0000000..de802be --- /dev/null +++ b/.storybook/stories/Visualize/Intro.mdx @@ -0,0 +1,7 @@ +import { Meta, Story } from '@storybook/blocks'; + + + +# Visualize + +This section shall explore the various setups and configurations of the visualization system. diff --git a/.storybook/stories/Visualize/Multiverse/Multiverse.mdx b/.storybook/stories/Visualize/Multiverse/Multiverse.mdx new file mode 100644 index 0000000..1f564eb --- /dev/null +++ b/.storybook/stories/Visualize/Multiverse/Multiverse.mdx @@ -0,0 +1,27 @@ +import { Meta, Story } from '@storybook/blocks'; + +import * as MultiverseStories from "./Multiverse.stories"; + + + +# Multiverse + +TODO + +## Single Universe - 2D +```js + +``` +
+ +## Multiverse - 2D +```js + +``` +
+ +## Multiverse - 3D +```js + +``` +
\ No newline at end of file diff --git a/.storybook/stories/Visualize/Multiverse/Multiverse.stories.tsx b/.storybook/stories/Visualize/Multiverse/Multiverse.stories.tsx new file mode 100644 index 0000000..eaf8801 --- /dev/null +++ b/.storybook/stories/Visualize/Multiverse/Multiverse.stories.tsx @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Simulation } from '../../Simulation'; +import { fig8, multiFig8 } from '../../Universe'; + +const meta = { + title: 'Visualize/Multiverse', + component: Simulation, + parameters: { + layout: 'centered', + controls: { + disable: true, + } + }, + tags: [], + argTypes: { + }, + args: { }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const SingleUniverse: Story = { + args: { + storyName: 'SingleUniverse', + universe: [fig8], + }, +}; + +export const Multiverse: Story = { + args: { + storyName: 'Multiverse', + universe: multiFig8, + // showTrails: true, + }, +}; + +export const Multiverse3D: Story = { + args: { + storyName: 'Multiverse3D', + universe: multiFig8, + visType: '3D', + showTrails: true, + showDebugInfo: true, + }, +}; \ No newline at end of file diff --git a/.storybook/stories/Visualize/Record/Record.mdx b/.storybook/stories/Visualize/Record/Record.mdx new file mode 100644 index 0000000..8e834fa --- /dev/null +++ b/.storybook/stories/Visualize/Record/Record.mdx @@ -0,0 +1,9 @@ +import { Meta, Story } from '@storybook/blocks'; + +import * as RecordStories from "./Record.stories"; + + + + + + \ No newline at end of file diff --git a/.storybook/stories/Visualize/Record/Record.stories.tsx b/.storybook/stories/Visualize/Record/Record.stories.tsx new file mode 100644 index 0000000..0bb8d1f --- /dev/null +++ b/.storybook/stories/Visualize/Record/Record.stories.tsx @@ -0,0 +1,49 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Simulation } from '../../Simulation'; +import { fig8, multiFig8 } from '../../Universe'; + +const meta = { + title: 'Visualize/Record', + component: Simulation, + parameters: { + layout: 'centered', + controls: { + disable: true, + } + }, + tags: [], + argTypes: { + }, + args: { }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const RealTime: Story = { + args: { + storyName: 'RealTime', + universe: [fig8], + visType: '3D', + }, +}; + +export const Recorded: Story = { + args: { + storyName: 'Recorded', + universe: [fig8], + visType: '3D', + record: true, + looped: false, + }, +}; + +export const RecordedLooped: Story = { + args: { + storyName: 'RecordedLooped', + universe: [fig8], + visType: '3D', + record: true, + looped: true, + }, +}; \ No newline at end of file diff --git a/.storybook/stories/Visualize/Trails/Trails.mdx b/.storybook/stories/Visualize/Trails/Trails.mdx new file mode 100644 index 0000000..cb39e02 --- /dev/null +++ b/.storybook/stories/Visualize/Trails/Trails.mdx @@ -0,0 +1,8 @@ +import { Meta, Story } from '@storybook/blocks'; + +import * as TrailsStories from "./Trails.stories"; + + + + + \ No newline at end of file diff --git a/.storybook/stories/Visualize/Trails/Trails.stories.tsx b/.storybook/stories/Visualize/Trails/Trails.stories.tsx new file mode 100644 index 0000000..e6e60ac --- /dev/null +++ b/.storybook/stories/Visualize/Trails/Trails.stories.tsx @@ -0,0 +1,38 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Simulation } from '../../Simulation'; +import { fig8 } from '../../Universe'; + +const meta = { + title: 'Visualize/Trails', + component: Simulation, + parameters: { + layout: 'centered', + controls: { + disable: true, + } + }, + tags: [], + argTypes: { + }, + args: { }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const ShowTrailsOff: Story = { + args: { + storyName: 'ShowTrailsOff', + universe: [fig8], + visType: '3D', + }, +}; + +export const ShowTrailsOn: Story = { + args: { + storyName: 'ShowTrailsOn', + universe: [fig8], + visType: '3D', + showTrails: true, + }, +}; \ No newline at end of file diff --git a/.storybook/stories/assets/accessibility.png b/.storybook/stories/assets/accessibility.png deleted file mode 100644 index 6ffe6fe..0000000 Binary files a/.storybook/stories/assets/accessibility.png and /dev/null differ diff --git a/.storybook/stories/assets/accessibility.svg b/.storybook/stories/assets/accessibility.svg deleted file mode 100644 index a328883..0000000 --- a/.storybook/stories/assets/accessibility.svg +++ /dev/null @@ -1,5 +0,0 @@ - - Accessibility - - - \ No newline at end of file diff --git a/.storybook/stories/assets/addon-library.png b/.storybook/stories/assets/addon-library.png deleted file mode 100644 index 95deb38..0000000 Binary files a/.storybook/stories/assets/addon-library.png and /dev/null differ diff --git a/.storybook/stories/assets/assets.png b/.storybook/stories/assets/assets.png deleted file mode 100644 index cfba681..0000000 Binary files a/.storybook/stories/assets/assets.png and /dev/null differ diff --git a/.storybook/stories/assets/context.png b/.storybook/stories/assets/context.png deleted file mode 100644 index e5cd249..0000000 Binary files a/.storybook/stories/assets/context.png and /dev/null differ diff --git a/.storybook/stories/assets/discord.svg b/.storybook/stories/assets/discord.svg deleted file mode 100644 index 1204df9..0000000 --- a/.storybook/stories/assets/discord.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/.storybook/stories/assets/docs.png b/.storybook/stories/assets/docs.png deleted file mode 100644 index a749629..0000000 Binary files a/.storybook/stories/assets/docs.png and /dev/null differ diff --git a/.storybook/stories/assets/figma-plugin.png b/.storybook/stories/assets/figma-plugin.png deleted file mode 100644 index 8f79b08..0000000 Binary files a/.storybook/stories/assets/figma-plugin.png and /dev/null differ diff --git a/.storybook/stories/assets/share.png b/.storybook/stories/assets/share.png deleted file mode 100644 index 8097a37..0000000 Binary files a/.storybook/stories/assets/share.png and /dev/null differ diff --git a/.storybook/stories/assets/styling.png b/.storybook/stories/assets/styling.png deleted file mode 100644 index d341e82..0000000 Binary files a/.storybook/stories/assets/styling.png and /dev/null differ diff --git a/.storybook/stories/assets/testing.png b/.storybook/stories/assets/testing.png deleted file mode 100644 index d4ac39a..0000000 Binary files a/.storybook/stories/assets/testing.png and /dev/null differ diff --git a/.storybook/stories/assets/theming.png b/.storybook/stories/assets/theming.png deleted file mode 100644 index 1535eb9..0000000 Binary files a/.storybook/stories/assets/theming.png and /dev/null differ diff --git a/.storybook/stories/assets/tutorials.svg b/.storybook/stories/assets/tutorials.svg deleted file mode 100644 index 4b2fc7c..0000000 --- a/.storybook/stories/assets/tutorials.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/.storybook/stories/assets/youtube.svg b/.storybook/stories/assets/youtube.svg deleted file mode 100644 index 33a3a61..0000000 --- a/.storybook/stories/assets/youtube.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/dist/src/index.js b/dist/src/index.js index 6c00158..4cf738f 100644 --- a/dist/src/index.js +++ b/dist/src/index.js @@ -9,9 +9,9 @@ import { ExplicitEulerSim, RungeKutta4Sim, SemiImplicitEulerSim, VelocityVerletS import { LambdaSim } from './SimulateFunction'; import { Simulation } from './Simulation'; import { State } from './State'; -import { BodyCenterTransformation, CoMTransformation, RotateTransformation, } from './library/Transformation'; +import { BodyCenterTransformation, CoMTransformation, PinTransformation, RotateTransformation, TimedRotateTransformation, } from './library/Transformation'; import { LambdaTransformation } from './Transformation'; import { Universe } from './Universe'; import { RealTimeVisualizer, RealTimeVisualizer3D, RecordingVisualizer, RecordingVisualizer3D, } from './library/Visualizer'; import { Vector3 } from 'three'; -export { BodyCenterTransformation, CelestialBody, CentripetalForce, CombinedForce, CoMTransformation, ExplicitEulerSim, Gravity, LambdaForce, LambdaSim, LambdaTransformation, RealTimeVisualizer, RealTimeVisualizer3D, RecordingVisualizer, RecordingVisualizer3D, RotateTransformation, RungeKutta4Sim, SemiImplicitEulerSim, Simulation, State, Universe, Vector3, VelocityVerletSim, }; +export { BodyCenterTransformation, CelestialBody, CentripetalForce, CombinedForce, CoMTransformation, ExplicitEulerSim, Gravity, LambdaForce, LambdaSim, LambdaTransformation, PinTransformation, RealTimeVisualizer, RealTimeVisualizer3D, RecordingVisualizer, RecordingVisualizer3D, RotateTransformation, RungeKutta4Sim, SemiImplicitEulerSim, Simulation, State, TimedRotateTransformation, Universe, Vector3, VelocityVerletSim, }; diff --git a/dist/src/library/SimulateFunction.js b/dist/src/library/SimulateFunction.js index 4c5b59d..c7c9b78 100644 --- a/dist/src/library/SimulateFunction.js +++ b/dist/src/library/SimulateFunction.js @@ -1,5 +1,6 @@ import { Vector3 } from 'three'; import { State } from '../State'; +import { Gravity } from './Force'; // export class VerletSim implements SimulateFunction { // forceCalculator: Force; // prevDeltaT: number | undefined = undefined; @@ -84,7 +85,7 @@ export class VelocityVerletSim { * Create a new VelocityVerletSim with the provided force calculator, which is invoked on every simulation step. * @param forceCalculator force calculator. */ - constructor(forceCalculator) { + constructor(forceCalculator = new Gravity()) { this.forceCalculator = forceCalculator; } /** @@ -143,7 +144,7 @@ export class ExplicitEulerSim { * Create a new ExplicitEulerSim with the provided force calculator, which is invoked on every simulation step. * @param force force calculator. */ - constructor(force) { + constructor(force = new Gravity()) { this.force = force; } /** @@ -188,7 +189,7 @@ export class SemiImplicitEulerSim { * Create a new SemiImplicitEulerSim with the provided force calculator, which is invoked on every simulation step. * @param force force calculator. */ - constructor(force) { + constructor(force = new Gravity()) { this.force = force; } /** @@ -236,7 +237,7 @@ export class RungeKutta4Sim { * @param force force calculator. * @param weights weights for weighted average. */ - constructor(force, weights) { + constructor(force = new Gravity(), weights = [1, 2, 2, 1]) { this.force = force; if (weights.length !== 4) { throw new Error('Weights for RK4 must be of length 4'); diff --git a/dist/src/library/Transformation.js b/dist/src/library/Transformation.js index d418405..1d4e9f4 100644 --- a/dist/src/library/Transformation.js +++ b/dist/src/library/Transformation.js @@ -1,16 +1,23 @@ import { Vector3 } from 'three'; /** - * Frame of reference transformation to the center of the first body in the system. + * Frame of reference transformation to the center of body i in the system. * @category Transformations */ export class BodyCenterTransformation { + /** + * Create a new BodyCenterTransformer. + * @param index index of the body to transform to. + */ + constructor(index) { + this.index = index; + } /** * Transform the frame of reference to the center of the first body in the system. * @param state state to transform. * @returns transformed state. */ transform(state) { - const transform = state.bodies[0].position.clone(); + const transform = state.bodies[this.index].position.clone(); state.bodies.forEach((b) => { b.position.sub(transform); }); @@ -70,21 +77,64 @@ export class RotateTransformation { return state; } } -// export class PinTransformer implements Transformer { -// readonly axis: Vector3; -// readonly index: number; -// constructor(axis: Vector3, index: number) { -// this.axis = axis; -// this.index = index -// } -// transform(state: State): State { -// const angle = state.bodies[this.index].position.clone().angleTo(this.axis); -// const pivot = state.bodies[this.index].position.clone().cross(this.axis.clone()).normalize(); -// state.bodies.forEach((b) => { -// b.position.applyAxisAngle(pivot.clone(), angle); -// b.velocity.applyAxisAngle(pivot.clone(), angle); -// b.acceleration.applyAxisAngle(pivot.clone(), angle); -// }); -// return state; -// } -// } +/** + * Frame of reference transformation to a pin body i to the given axis. + */ +export class PinTransformation { + /** + * Create a new PinTransformer. + * @param axis axis to pin to. + * @param index index of the body to pin. + */ + constructor(axis, index) { + this.axis = axis; + this.index = index; + } + /** + * Transform the frame of reference to a pin body i to the given axis. + * @param state state to transform. + * @returns transformed state. + */ + transform(state) { + const angle = state.bodies[this.index].position.clone() + .angleTo(this.axis.clone()); + const pivot = state.bodies[this.index].position.clone() + .cross(this.axis.clone()) + .normalize(); + state.bodies.forEach((b) => { + b.position.applyAxisAngle(pivot.clone(), angle); + b.velocity.applyAxisAngle(pivot.clone(), angle); + b.acceleration.applyAxisAngle(pivot.clone(), angle); + }); + return state; + } +} +/** + * Frame of reference transformation to rotate around an axis by 360 degrees in a given time. + */ +export class TimedRotateTransformation { + /** + * Create a new TimedRotateTransformer. + * @param axis axis to rotate around. + * @param revolutionTime time in seconds for one full revolution. + */ + constructor(axis, revolutionTime) { + this.axis = axis; + this.revolutionTime = revolutionTime; + } + /** + * Transform the frame of reference to rotate around an axis by an angle determined by the time elapsed. + * @param state state to transform. + * @param deltaT time elapsed. + * @returns transformed state. + */ + transform(state, deltaT) { + const angle = -(deltaT / this.revolutionTime) * Math.PI * 2; + state.bodies.forEach((b) => { + b.position.applyAxisAngle(this.axis, angle); + b.velocity.applyAxisAngle(this.axis, angle); + b.acceleration.applyAxisAngle(this.axis, angle); + }); + return state; + } +} diff --git a/dist/src/library/Visualizer.js b/dist/src/library/Visualizer.js index 42764e9..a8aea83 100644 --- a/dist/src/library/Visualizer.js +++ b/dist/src/library/Visualizer.js @@ -283,17 +283,20 @@ export class RealTimeVisualizer { * Stop the simulation and visualization. */ stop() { - console.log('stopping in viz'); if (this.animationId === null) { return; } cancelAnimationFrame(this.animationId); - Plotly.purge(this.divId); this.divId = ''; this.universeTrails.forEach((ut) => { ut.popAllTrails(); }); this.universeTrails = []; + try { + Plotly.purge(this.divId); + } + catch (_) { + } } } /** @@ -360,7 +363,10 @@ export class RealTimeVisualizer3D { */ constructor(simulation) { this.animationId = null; - this.renderer = null; + /** + * Clear the visualization. + */ + this.clear = () => { }; this.universeTrails = []; this.simulation = simulation; } @@ -428,10 +434,10 @@ export class RealTimeVisualizer3D { this.scene = new THREE.Scene(); const camera = new THREE.OrthographicCamera(width / -2, width / 2, height / 2, height / -2, 0, 10000000000); camera.position.set(0, 0, Math.max(width, height)); - this.renderer = new THREE.WebGLRenderer(); - this.renderer.setSize(width, height); - this.renderer.autoClear = false; - element.appendChild(this.renderer.domElement); + const renderer = new THREE.WebGLRenderer(); + renderer.setSize(width, height); + renderer.autoClear = false; + element.appendChild(renderer.domElement); let stats; if (this.simulation.showDebugInfo) { stats = new Stats(); @@ -460,22 +466,21 @@ export class RealTimeVisualizer3D { // labelRenderer.domElement.style.position = 'absolute'; // labelRenderer.domElement.style.top = '0px'; // element.appendChild(labelRenderer.domElement); - const orbitControls = new OrbitControls(camera, this.renderer.domElement); + const orbitControls = new OrbitControls(camera, renderer.domElement); orbitControls.listenToKeyEvents(window); orbitControls.update(); const axesHelper = new THREE.AxesHelper(width); this.scene.add(axesHelper); - const viewHelper = new ViewHelper(camera, this.renderer.domElement); + const viewHelper = new ViewHelper(camera, renderer.domElement); // var m: Map = new Map(); let arr = []; this.simulation.universes.forEach((u) => { this.universeTrails.push(new ThreeUniverseTrail(this.simulation.maxTrailLength, typeof u.color === 'string' ? u.color : u.color[0], this.scene, scale)); - u.currState.bodies.forEach((b) => { + u.currState.bodies.forEach((b, i) => { const sph = new THREE.SphereGeometry(clipMinMax(Math.log2(b.mass) - 70, 10, 40), 8, 8); const curr = new THREE.WireframeGeometry(sph); const line = new THREE.LineSegments(curr, new THREE.LineBasicMaterial({ - // @ts-ignore - color: new THREE.Color(u.color), + color: new THREE.Color(typeof u.color === 'string' ? u.color : u.color[i]), })); this.scene.add(line); line.position.copy(b.position.clone() @@ -506,9 +511,9 @@ export class RealTimeVisualizer3D { if (this.simulation.controls.speed === 0 || this.simulation.controls.paused) { this.animationId = requestAnimationFrame(paint); - this.renderer.clear(); - this.renderer.render(this.scene, camera); - viewHelper.render(this.renderer); + renderer.clear(); + renderer.render(this.scene, camera); + viewHelper.render(renderer); // labelRenderer.render(scene, camera); orbitControls.update(); return; @@ -516,9 +521,9 @@ export class RealTimeVisualizer3D { step(timestampMs); if (timePerFrame > 0 && timestampMs - lastPaint < timePerFrame) { this.animationId = requestAnimationFrame(paint); - this.renderer.clear(); - this.renderer.render(this.scene, camera); - viewHelper.render(this.renderer); + renderer.clear(); + renderer.render(this.scene, camera); + viewHelper.render(renderer); // labelRenderer.render(scene, camera); orbitControls.update(); return; @@ -548,28 +553,48 @@ export class RealTimeVisualizer3D { } }); this.animationId = requestAnimationFrame(paint); - this.renderer.clear(); - this.renderer.render(this.scene, camera); - viewHelper.render(this.renderer); + renderer.clear(); + renderer.render(this.scene, camera); + viewHelper.render(renderer); // labelRenderer.render(scene, camera); orbitControls.update(); }; + /** + * Clear the objects and renderer. + */ + this.clear = () => { + renderer.clear(); + renderer.dispose(); + arr.forEach((a) => { + if (Array.isArray(a.material)) { + a.material.forEach((m) => { + m.dispose(); + }); + } + else { + a.material.dispose(); + } + a.geometry.dispose(); + }); + }; this.animationId = requestAnimationFrame(paint); } /** * Stop the simulation and visualization. */ stop() { - var _a, _b, _c; + var _a; if (this.animationId === null) { return; } cancelAnimationFrame(this.animationId); - (_a = this.renderer) === null || _a === void 0 ? void 0 : _a.clear(); - (_b = this.renderer) === null || _b === void 0 ? void 0 : _b.dispose(); - (_c = this.scene) === null || _c === void 0 ? void 0 : _c.clear(); + (_a = this.scene) === null || _a === void 0 ? void 0 : _a.clear(); this.scene = undefined; - this.renderer = null; + this.clear(); + /** + * Replace back the empty function. + */ + this.clear = () => { }; this.universeTrails.forEach((ut) => { ut.popAllTrails(); }); @@ -807,9 +832,13 @@ export class RecordingVisualizer { return; } cancelAnimationFrame(this.animationId); - Plotly.purge(this.divId); this.divId = ''; this.universeTrails = []; + try { + Plotly.purge(this.divId); + } + catch (_) { + } } } /** @@ -823,6 +852,10 @@ export class RecordingVisualizer3D { */ constructor(simulation) { this.animationId = null; + /** + * Clear the visualization. + */ + this.clear = () => { }; this.universeTrails = []; this.simulation = simulation; } @@ -932,12 +965,11 @@ export class RecordingVisualizer3D { let arr = []; this.simulation.universes.forEach((u) => { this.universeTrails.push(new ThreeUniverseTrail(this.simulation.maxTrailLength, typeof u.color === 'string' ? u.color : u.color[0], this.scene, scale)); - u.currState.bodies.forEach((b) => { + u.currState.bodies.forEach((b, i) => { const sph = new THREE.SphereGeometry(clipMinMax(Math.log2(b.mass) - 70, 10, 40), 8, 8); const curr = new THREE.WireframeGeometry(sph); const line = new THREE.LineSegments(curr, new THREE.LineBasicMaterial({ - // @ts-ignore - color: new THREE.Color(u.color), + color: new THREE.Color(typeof u.color === 'string' ? u.color : u.color[i]), })); this.scene.add(line); line.position.copy(b.position.clone() @@ -1022,6 +1054,24 @@ export class RecordingVisualizer3D { // labelRenderer.render(scene, camera); orbitControls.update(); }; + /** + * Clear the objects and renderer. + */ + this.clear = () => { + renderer.clear(); + renderer.dispose(); + arr.forEach((a) => { + if (Array.isArray(a.material)) { + a.material.forEach((m) => { + m.dispose(); + }); + } + else { + a.material.dispose(); + } + a.geometry.dispose(); + }); + }; this.animationId = requestAnimationFrame(paint); } /** @@ -1035,6 +1085,11 @@ export class RecordingVisualizer3D { cancelAnimationFrame(this.animationId); (_a = this.scene) === null || _a === void 0 ? void 0 : _a.clear(); this.scene = undefined; + this.clear(); + /** + * Replace back the empty function. + */ + this.clear = () => { }; this.universeTrails.forEach((ut) => { ut.popAllTrails(); }); diff --git a/dist/types/src/index.d.ts b/dist/types/src/index.d.ts index 4e74496..d0c92bc 100644 --- a/dist/types/src/index.d.ts +++ b/dist/types/src/index.d.ts @@ -9,9 +9,9 @@ import { ExplicitEulerSim, RungeKutta4Sim, SemiImplicitEulerSim, VelocityVerletS import { LambdaSim, type SimulateFunction } from './SimulateFunction'; import { Simulation, type ControllerType, type VisType } from './Simulation'; import { State } from './State'; -import { BodyCenterTransformation, CoMTransformation, RotateTransformation } from './library/Transformation'; +import { BodyCenterTransformation, CoMTransformation, PinTransformation, RotateTransformation, TimedRotateTransformation } from './library/Transformation'; import { LambdaTransformation, type Transformation } from './Transformation'; import { Universe, type UniverseConfig } from './Universe'; import { RealTimeVisualizer, RealTimeVisualizer3D, RecordingVisualizer, RecordingVisualizer3D } from './library/Visualizer'; import { Vector3 } from 'three'; -export { BodyCenterTransformation, CelestialBody, CentripetalForce, CombinedForce, CoMTransformation, ExplicitEulerSim, Gravity, LambdaForce, LambdaSim, LambdaTransformation, RealTimeVisualizer, RealTimeVisualizer3D, RecordingVisualizer, RecordingVisualizer3D, RotateTransformation, RungeKutta4Sim, SemiImplicitEulerSim, Simulation, State, Universe, Vector3, VelocityVerletSim, type ControllerType, type Force, type SimulateFunction, type Transformation, type UniverseConfig, type VisType, }; +export { BodyCenterTransformation, CelestialBody, CentripetalForce, CombinedForce, CoMTransformation, ExplicitEulerSim, Gravity, LambdaForce, LambdaSim, LambdaTransformation, PinTransformation, RealTimeVisualizer, RealTimeVisualizer3D, RecordingVisualizer, RecordingVisualizer3D, RotateTransformation, RungeKutta4Sim, SemiImplicitEulerSim, Simulation, State, TimedRotateTransformation, Universe, Vector3, VelocityVerletSim, type ControllerType, type Force, type SimulateFunction, type Transformation, type UniverseConfig, type VisType, }; diff --git a/dist/types/src/library/SimulateFunction.d.ts b/dist/types/src/library/SimulateFunction.d.ts index a44563e..a6f5fa6 100644 --- a/dist/types/src/library/SimulateFunction.d.ts +++ b/dist/types/src/library/SimulateFunction.d.ts @@ -14,7 +14,7 @@ export declare class VelocityVerletSim implements SimulateFunction { * Create a new VelocityVerletSim with the provided force calculator, which is invoked on every simulation step. * @param forceCalculator force calculator. */ - constructor(forceCalculator: Force); + constructor(forceCalculator?: Force); /** * Simulate a step in the Universe by using the previous and/or current state and a time step, using the Velocity Verlet integration method. * @param deltaT time step. @@ -48,7 +48,7 @@ export declare class ExplicitEulerSim implements SimulateFunction { * Create a new ExplicitEulerSim with the provided force calculator, which is invoked on every simulation step. * @param force force calculator. */ - constructor(force: Force); + constructor(force?: Force); /** * Simulate a step in the Universe by using the current state and a time step, using the Euler integration method. * @param deltaT time step. @@ -79,7 +79,7 @@ export declare class SemiImplicitEulerSim implements SimulateFunction { * Create a new SemiImplicitEulerSim with the provided force calculator, which is invoked on every simulation step. * @param force force calculator. */ - constructor(force: Force); + constructor(force?: Force); /** * Simulate a step in the Universe by using the current state and a time step, using the Semi-Implicit Euler integration method. * @param deltaT time step. @@ -115,7 +115,7 @@ export declare class RungeKutta4Sim implements SimulateFunction { * @param force force calculator. * @param weights weights for weighted average. */ - constructor(force: Force, weights: number[]); + constructor(force?: Force, weights?: number[]); /** * Simulate a step in the Universe by using the current state and a time step, using the Runge-Kutta 4 integration method. * @param deltaT time step. diff --git a/dist/types/src/library/Transformation.d.ts b/dist/types/src/library/Transformation.d.ts index 7911107..ba2d02f 100644 --- a/dist/types/src/library/Transformation.d.ts +++ b/dist/types/src/library/Transformation.d.ts @@ -2,10 +2,16 @@ import { Vector3 } from 'three'; import { type State } from '../State'; import { type Transformation } from '../Transformation'; /** - * Frame of reference transformation to the center of the first body in the system. + * Frame of reference transformation to the center of body i in the system. * @category Transformations */ export declare class BodyCenterTransformation implements Transformation { + readonly index: number; + /** + * Create a new BodyCenterTransformer. + * @param index index of the body to transform to. + */ + constructor(index: number); /** * Transform the frame of reference to the center of the first body in the system. * @param state state to transform. @@ -45,3 +51,42 @@ export declare class RotateTransformation implements Transformation { */ transform(state: State): State; } +/** + * Frame of reference transformation to a pin body i to the given axis. + */ +export declare class PinTransformation implements Transformation { + readonly axis: Vector3; + readonly index: number; + /** + * Create a new PinTransformer. + * @param axis axis to pin to. + * @param index index of the body to pin. + */ + constructor(axis: Vector3, index: number); + /** + * Transform the frame of reference to a pin body i to the given axis. + * @param state state to transform. + * @returns transformed state. + */ + transform(state: State): State; +} +/** + * Frame of reference transformation to rotate around an axis by 360 degrees in a given time. + */ +export declare class TimedRotateTransformation implements Transformation { + readonly axis: Vector3; + readonly revolutionTime: number; + /** + * Create a new TimedRotateTransformer. + * @param axis axis to rotate around. + * @param revolutionTime time in seconds for one full revolution. + */ + constructor(axis: Vector3, revolutionTime: number); + /** + * Transform the frame of reference to rotate around an axis by an angle determined by the time elapsed. + * @param state state to transform. + * @param deltaT time elapsed. + * @returns transformed state. + */ + transform(state: State, deltaT: number): State; +} diff --git a/dist/types/src/library/Visualizer.d.ts b/dist/types/src/library/Visualizer.d.ts index 11da1c7..68b6647 100644 --- a/dist/types/src/library/Visualizer.d.ts +++ b/dist/types/src/library/Visualizer.d.ts @@ -100,7 +100,10 @@ declare class ThreeUniverseTrail { */ export declare class RealTimeVisualizer3D implements Visualizer { animationId: number | null; - renderer: THREE.WebGLRenderer | null; + /** + * Clear the visualization. + */ + clear: () => void; simulation: Simulation; scene?: THREE.Scene; universeTrails: ThreeUniverseTrail[]; @@ -164,6 +167,10 @@ export declare class RecordingVisualizer implements Visualizer { */ export declare class RecordingVisualizer3D implements Visualizer { animationId: number | null; + /** + * Clear the visualization. + */ + clear: () => void; simulation: Simulation; scene?: THREE.Scene; universeTrails: ThreeUniverseTrail[]; diff --git a/docs/api/assets/navigation.js b/docs/api/assets/navigation.js index 033ebc0..33c51a6 100644 --- a/docs/api/assets/navigation.js +++ b/docs/api/assets/navigation.js @@ -1 +1 @@ -window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAACq2WTU/bQBCG/0q1Z0NKAm3xkfAhRHuBNBfkw2Y9SUbsR7Q7Rkmr/PfKNCS298MguPp95/HM7szYj38ZwZpYzi4qlCXqxZeZNOLJsYyJJcrSgmb54941BgmOkMsLU25YxlaclixnQnLnwA1a8vGSlGQZe0Jdsvxk+GOb7TkPqCrJCY32IQctSSBOEAiuH6fifmt8BusCoa9KKnoKgowd+cE7wYvN/jtYzsiVR+iOYE1gNZdsWzS4t5rAzrmA2MFfGysaOePeP3hR2u8dnn3zzxquKy3aJ96gdE0p4MRy7ebGKh7FtS0+rFn7SwHxhtNkcQXEZecIDj3XdqSub2zUDDWUMVRTTnFuLH9GCgzATkjF/uRqVvJIBg0xxXh3NxSJfogd/dV6JVEgXVUS7AMqP9uuo7/sIGYvpeLvK72Au4qInwYhbT25OUDhreqrLeRKbwZpBNJmClYCBZGe5Q0b8qNTW0TnNnbt9equZwpsbM5f64k50xP4qw/rWfrbqg8ZciWbzdSfkT5qyJWifvLenGxW8bVpNFkjJdjadXgV1TGDttp5y9fz7ydnw8AXc2z0HBddVlvtY03RhRLaPQ5HF+34ikv8AzZW+T1wOUEFB2fg4jxPshk89+jyLczRZZoqjK1/uNKJeqZ3MsOpBmw+t9gW/wBFz1T7HwoAAA==" \ No newline at end of file +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAACq2WTW/bMAyG/8qgs7usyboPH5u2Q7ENGNIsl8IHxWYSorIUSHSRbMh/H9xmiW1JdILtar58RIof8uNvQbAhkYrrClWBevlmrkz+5EQi8hWqwoIW6eNBNQYFjlCqa1NsRSLWklYiFbmSzoEbtMxvV1QqkYgn1IVIL4efdsmB8wP11ErtFsaWktBon+VJON4DlpWKgI42lkCSIOBcf+b8plhCMTG1rC+jqJTj/9T4DNYFQvtr4bxnkJOxI995b/B8k1eFSAW54gLdBWwIrJZK7LIG914T2IXMIdYod8bmjZjxoB+8WNrnDq8++LWEu0rn7YtsULoiDhirSwPH1aOGNXN/SSA+IJosroGk6lzBcUbaCq58Y1POUUMRQzXNHOeLlc9IgYHdGzjfb7KcFzISQcPIMc7uhozph9jV327WCnOk20qBfcDSj7ar6E87iDmYOP9JpZfwtSKS74OQtp3dTFDifdmXW0jFbwZlcqTtDKwCCiI9yQkb+F+nNovObazs9VNTzxTYvv0bU/IT+L0P60n626oPGVKxzXbS+3Pu0/Of9+Z0u46vTaPJGqXA1qrjUVT7DNrWzinvPn+8vBoGXsyx0Qtcdlltax9rhi4U0P5z2Dtr+1dS4S+wscwnIFX9Y3BUBgrnadhm8NSjm1OYoxuemhtb/yDygXqiM5nhUAMyn5vtsj+l4xiozwoAAA==" \ No newline at end of file diff --git a/docs/api/assets/search.js b/docs/api/assets/search.js index 0023137..c4c4a45 100644 --- a/docs/api/assets/search.js +++ b/docs/api/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAACtV9a48jN5Lgf5ExuPuQKyffZH88z+5gcLvAYWfOXwyjoVZlV2utkmr1qLbX6P9+iOAjSSYjlaoqz9196mwlyQgG4x3BrN9Xp+PX8+rDT7+vftkdHlYfGLfd6rB5GlYfVv/j+PDbD8PhMpz+ftoczp+Pp6fNZXc8rLrV9bRffVht95vzeTh/Tw1cf7k87VddHLf6sFp96yIkxXiCtD0ezpfTdXs5nu5b/LtyZgaoWz1vTsPhMrePERney3HflzjqTlzyea/GJD+AH4b9cL7sNnuYN0GmePsupJ6uuIi+JZrZVnouE9z95tOwXwIxDnwDrKfN+bwEVBj3BkjPx/OuKRINaNnYN0B8GfbH7e6yhB2+y8a+AeJmux32w6kt+g2o1fj7IBeCuN0fD8MiLg0D79xlIWqHy2n3PFw2+385nrYtqOWAdxK4xqILZa7Clzi8LWqbpYDj4LthFsf2OFzwfUsGW2Dz8ffvNj/E49On3WF4IE4wf/s+xzdZcdnZFWgSB/eZoOAU5OcbtCOgLT2yKcAF50XtsDisf7vhVkxGvNOhtVZdeHA1ynd7EAT0267DHOycrP/86/N+t91d/vm6H05/200xqAe8C1Gbiy6i6QTfOXlYCDeOvRticYjn3dN1v7ksBZoNv3+n2QH+5bR5adn38Pu7HFe+1qJTikgRh/OXeQh/uWPdhWqpWP+2Qmrgn5H8XzdPnx42bauRvXsX0tfrLSJ/jiAlH1P9OQH1mfbH2hC0UkInEB8/Xn57vk2h7z4f1mnoTWjrz6QWpU9/AvI2BxAknHBBS2WmN+/IAXcpyRG1e09/1Im3zr5c/Y6Tz0AsO3eYQJ86qXYraDf1bZNok/O+4YO0Br0jF7zWE2nifi9vtIDfZBMS5h0c0wa8jHmquTQf0T4YjcRtN+wW4TP++vdhs//77mn4cXe+bva7/2qEYdMh78JbxLKLOKuBNZUXOOz8/v/6sBiDcs5bMQgqoCW3FALFlLfCf9i93LH3OPqtUK+H3ctwOg9/P212+6ltpMBPpr0Cj0LANg8PPxwPl9PxDiTKOSMGl/PDP+3O//R82r14lf4KfM6XzemynBHC6LdS4Xw5Pt8BFAe/hgNmFYv48wIUxJ//IOUSFn6lehF/fh8FE7F4nYqhsTgNh4fhtEh5RxSyKW+Hf5eSixi8Ss3N4LAdGonYGfBh/Nsh363sIgqvVncFLm9QeIkf30Hl0TgtVXrpZO5Ue3OQFym+EfBdqq/kiEL5bY+nh93hcdatmox5J9XXXneh5psi/irFR+CwTO8txmFW7RAoLNI6izGgvCsC+C33ajHcmyqHQGCxxpnD5A6FQ/HBK/TNYowodUMxxA1tcwfcprIhwc7qmllOmFc1TUerMeqPUjf3uVot9N9H5dznbN2Bx31q5y535x4sCH+HRuCGw3MH7PvVz90uzzw2b1FBb3F67sBqsRpa6vbcBXuZKlro+NzgjFwdHS+by3AjX9ka9D7KiFp4mS5q4U6pol93DV4jwYfh7wD38LhvCD0NOIx/FeSFqUoa+u1U5a2956x1PTwO//N6uWxkq/5Rvn4fdpouuYyRSkzvKha3YM6XipdB+zrsHr9cGjzbgDeOvRPishpJC+TNQgm9y4xF/jY87f76dKO3oDXoXdiFXHgR0zRxv4t1aPjzDHQD8rJDpYHfPNpbO88PmPa8xlfvc5jlcsuOcMSOrhb/7XkYpu5rDS4b+FpY56Wwzm+HtTv/r/3mt93h8SawfORroT1vrucGB1aQ4qjXQjkN5+vTbTBp2Ft44svxK+FKtxgjH/0W7lgOtR79xr3+7xAELN1tNv6N+10MeTr+DXv+t82vSLp/HQ6Ply9Ldj2Z8YZ93wm9NePV0Jsx0ATibOCzBEoj2pkCmQlxmjAKw3NpGj349X3MTVppmaVBdAjv4NPxYdfoR8pApBHLVl/Q757jP9vnPsE8IzIpnPHFu5C6WGwRtRNe1AWL0/DSZpASVj7udZC219NpCaR83CshHfcLaLd/A9Xal21KCPP3bG5BOO+e/uV62N6AMY56HZRLEbxO5a6ENh29GOoCMazOZ1YSW/vKhPHHcC/nx+G0Hy6tQGoy4l3Es73qIjmdojwXP/2w2W9B6S/HYTrtfjyWRVMEAjdDqXnIzjA1HkLIfe6H09/zLjhob4MrAPnL2YPlauyoK9uTd3BT5/NmO5y/v92bfKOrtrnWgpbayY2SDNtg9QcQ/yKmzIDVY5bvYXK4t1ZdECnXCLf3RaRfM/h35F5vJAHnF12QAZzJPpb8GvXVD8fD591jxa/ly9kNzfaAkqt9d6Pxs8JusadwC+B6udMQJgdEF3sQNzFY7kwsw6DwLG5DX+RkLIJcehw3IS9zPhZBrj2Rm7CXOiWLoFMeyk0s7nVWKGxqUf5xd27YnPBrS3h/7la7w8Pw6+rD7yuABJriw4qvxdqtutXn3bB/gA8E+PW71fb49ASY/Rze/TiA3wAj/JDv+1X3U99JtTZM/Pxz91OcgS9Yx9ec8Y53bK161Ynwf9nxtWGd6tiaK9Hprl9rwzvT8bUwrrOdWCupOtfxdc861sN0xTsG61mlEQ5CZwCEd8yshWUFeHgzjuMwTnRcr7mSxTge0Ox7iWjKniGa8H/AjwmN+JmeIX5MacSPM9a5jq0tDwjKXnsElbUdwyWM7hhsVSrdMVhMGDXiJACy7oRZ8xIleAGocI4UEEJ0DEBzrTpmAScjO+Y6sTZGdLwHUEJ0nHViLZjqOO/kWgvWcdHJNQMUpH8YgUuScPAGNqx60zEN83ruwXJjPDTOOq5gXxpIGokHKII94LZT615kO1WwIddxuxbaFMBU2KrujecCJjvYWA8bwx3abB296n4SfSfk2vByHR3WUdyflrISaeeY7UTfSX+srFNr4zIqGFiQd7Jf6748AzO3IO/UmkndCaCv4eN6FtaTLQTt3Hp+mU5Ij+i4noP1VAs/t2Q95fHMxAWEUrR4Dt/gCXDpj50L5LZ+ba0Lx245sJQYWUozCwzA1sLBol46O2HjAwiIk7KTPWDiXCenEgyCKptngK9AbIyyuEmpQQbZ2mjrZY4pFA22llEyNOskyJ4GYoLsOa06ibIndCeRWZnsJEyyspMwSQOGgCrvXaf6MFux+MADJBUXVrAw47xTIAVc8U5p/0u2L06rJj4KuN+WRtViNBvlexRrFUQfCW1lkDTQCkA2PAzE3lqbgQclYjpuJgqPRfWiHYAVSAZgHc0zUWOgBpTpBI8Mnubjm65fu94GzW38qXCJGhG1HZ4OsBKeDqAIxFPaBpL3IiAtgOJi7azzFIchnuJceorjL0DxXjtPceZ0p2x8cGE93YdfgIxrYTI5Z6BndJvJvArq1xbWAolyxutxCQIA6GstgwK0zvOSAqUBRy4YA17iawYPgImwxuMvuPCYgJBoEJsezlt2Yi173mkVH3R8QB3fyw64e60K/DXNTDrX13eoaQ17FE52QJe1NCIDB9oP3rO1ERW5TGFXmSXZxAa6aue5XDmB7AF0QbJyGxSM1hqMF3BwIKJ2gQmAZAo25eBc+/gAg51lQFa5Vs552hlPOcdNp2EVY1Vn4hyD2xWyMxx53XZGxAfpH7JtgXo1quXW4CtcC8iMDoBFhwH+jwqHceQjIb1m1r1C6VYK/QOBbAxWbM2VyTyUnjxifJWNZLQzwwJyDHQwyqR3uuD/6G0x5pHyGkd4TkfF430z5T0Wzb3DIrUBXPnaZezBQX8Z3ZImzsOpOxBsVGX+9AX4Qqh0QY3BqTupvTBpy/xhS8U7Y1D0dWcsoq87A7qJSdlZwEMy21k0IkJ1YI/WnKvOgngZwTsrYR2rO6v8LxnWgiawCDKEWEbkOLCOBkKqgDdqYFid406sZ1bFmWc8xFYHJyxtBIZ7bE18sCiLrAPDir84NDqGd867opkLwEHdAhy71qIUMS6DfZRKJUOCmtf2DGjL173VoHCD5g1WwlMbxqAO4KB7UJtK3jl0KozLEAAFCXS1a+ZKpwZfebfNeMA9KE88VBMtMriuGmkhvEOgtATq9GurmD9LAGx5YAU8QhiMtHC9Bpz6tdO8cyKMcRLdEds5gNUD4XRY2Rk08a5zqAtY5mpwUJXghDT4FhUsTAXVg4gL5zwNJeipHjwYhdtkoC6d7MAvxt8yCIbmMRNoBfzj9TQPzjtj1itqEVjMgeY2aII6wGLNYQ54sGsnINwQ/rcMMGhb1suWxgrvRrRhtutYr9KWdHoy6cnCqAyCozQ9d0HmjQyhHDPeEQClbtAWOy9WBqITFg4IHQE4MuRLJp3nS8NEMO6SB5sODzgLrJX3MqVn2V5YUPF+DKp4CDMTV7DehXeMoUV02WEJ1Lhgt6f8gO/y8E+L4EaCwZA2Bog8BH5okQwLhkgAIdDNgaDWogn2egxCQtRjMIRBZOzDRdS+DgCg+mW5hyzoIBdfobOBRqdfK2ZDOKA7ByKG7AL4S+6PABQoh1/AXeMwx/Yoof1aK3AYwyzpZRYNMvh4Ajbr10GDzOEI+vjAYEGrYPvg0wVLDFiZ+OAURhcWD8VPA+Izr7WYRLPPOgYsgg4WYxhZSBhnUCpgHMYWOCMqT+YVNTwgk4gOXPPwi4gP0kPKyOoddNUKwHk4fcWCYvU6HwUSDx/Um1eeTvoT5gL1E19b0PyMg1MH0QmepzU533lDpFthIr7LhpJeOL5BD0mxlD9R3h4GaTHMwHmItYkKXgNfOp978ZRUuRIRChEzTYFQgSTcBQ4LrrDVgcUN8ywuTDDV2livzDWkclBSWOB5B047A6MtgGN8kkRnDq+gHV6ho4sRHEtQKp7HFQteC2RMkNlBrSKzCxWYHdjGe0CR2SXapX7tvGT7B8/sigXWBqcTFZhRwTORaKAgbLB+c+DOOoF4RbvkGNglv2DgelBKuF0OIShiC2M9q0Ocga6FtKaDCiJbMzBHPE0VfXwrWJwhkNkhJSSEf8roaGgmLz15YWe40pZDHekRiJiX6K3zhyND6kuZoAvAPy7ZAgmGak9mzIDbFusiaYUaWzStnOwrvxynV345yrII6azemoCRFZ51peaedQ38jjIMzrrnVOni0alcO0taO0sWOBUiSW8YXaaNZdRp3gm+Uz97lmVqVNSjfo6vTNTPSQlbn9BRwLvguXpGlWCwMUAEaczU86xSjiq1Y0IFLSs0xmzAiib+ZOODC3gyGfW1ZPGBx0VlVNgSYWcZCUnra3yVjZxRsLJUsFKSsZR/RTBUDOzQbgPbsMhi/jTFyFneV+V6ZJ6OSYUOXGYUJOpe2UrN4StcxWoF5yTWPehPabwfJ234F8TFgTOl+tJ3k7Q2ja/4WoIDBksosA4SXEIucwxRlSjWpGqpSyToCzCDIqVb08hSlUiMplUzM4PvsiQyCrgSLfiqjJAVyqRqJmIVa9h2pK6CBL6CuFEDA0L+MjyZ9GSjM66SW44uas/ydDdyqmYNs42vYr7WAlQBAQ6m+MBx0T7GdR3zWZk8ElS+YNBkViWydTH0NNK210Wry7ODVcjokJiasB6+arHehOUgywq/aF2ynlIk68VXNetpWbOe0jTr4btsKHKpNi0ToUouVTSXqpJLFXIpRNBTLlUll+oZLtUll+oZLtUp9S18TIVqQwGRGehjpdOTiU94xqLPEjWaZkT9BkbUM4yo38CI2jOia52dDpwo1n3vdZ8vwQALSu84qD7+Zvq68KFpNtQEG041oJ5hQ12yoTYkb+mSC7WlNaAu2VC7Gd4q2dD0NG+Z/g/XgIaRjIevXsl4htOMZ/jrGc8gQxvWYjx8lzFeYjetIrtpTTOeoQut8dVt/WcUzXj4LhuqScYzJYsaQys1U/KosTTjmZJHjZthPPceSs32JG/Z/vW8ZRnNW5a9nrcs8qzhLd7y70IVF11MAbUE/390KIXGtA5YX4APpR2sgGFoiaUN4115aWKeVLiQJ5UshKHShPQoOqEmrOudddPbjmElhIPrbWR6AjAG66s6PZn01qbfXHzykRM+Mf+UUYFOvvtXmAuEShqAdXB2AFVCDGx5p9f5SaHcGNHwV/BVNhLlxrQ8G1uKjdVkHOBfYarHN7JAaCvC/1F9QkgkQubDp3uAqDHoRX0KbQ0ixrUMtKmDnKhJKRBAMnVHeJRQPq1oyac1VdESwlRkUKTxWLME1hFjzTIVJrGeCmEpcnPPQySsjIRIGHISIYkD9Ugss/Q8ZHMERIZWhnomw0KLf9LpyfiSZrYZS5+/jcUXKUaUMacnnfDFF9iN7zNQ0hdfTKx6Y73VFzZ0qpyO28Lqi7Kh+oIPFqs50ldf4BesvgAxUJLgFbMi7QU5HXIv1vnfsl05sjZj3T+iNuNQF1rbYHB8hUZFWL8NuXaQzIBCO2YZmOtLwXKMzOu4qAF17MfqY2JHMxHyKSaUL3rjCz3SsVDoYSzUd3rhmag3vrxjmQ1qC1IQIgzxIXMvg/7iobwD6zmfCVa+vOOLiajo4VitSk/aP2WbQ13rmtl+F6uWwEsAUYQcZx8KGJqFuqAV1kuTtKFcAWk/lCahld+tc1GauPPSBDlC66sUoeKlIkWcCVIFEBzmRiGbgsrCP6FigFSDU/4p2xOtVd07ljTfVMnsgOZYEs3wpmua7h9S03R0TdOpN3M67lyKP5DlkVGQ0ZFRSkanPUB8xXxCTnhiggkyJnTteFtugt9ghQwRPhYynI7+vjN1+dHRdU+X6p553hUZ0YJh8d5MqBGb3vg6C4vlBah3In5QaHEht8aw8ASi5bCBCcyts3VKzaHNcc1w0tnUBBVqltKMYoI1EiWULy3aaCahfBLSpNARB0k2CyVbLMxC9wFwXdV14DAhDiXXBhJ1slzbAAmKAyhbnHlHDZhdIUyB0gWcgl4F71Wop/B+HGaCE8J7659GhFjfI0ZNsviXMbmpXchpIk6WScBJhLo1AMO+nT5kybG1ZN1DQ4mvfrm8G67HXDhrejP+pa+yOg9MuEAAaUUggDV+t6HHtR9/M0gKjbsNT0zUDa+sRyUPbkMLA/7uGExablmPnXes3VbUR4WN7iTqaeivc9Gd9CwPGns8Z6xp49kjxcGRBIZRqEg4gxyJxbfW/5bjgvVDRnBBVMKxXUhBhhu7sIPKB+OANIEGMo8b6N3eBy2BKw3+FEd5rtQ8MCU8IP44imETHOyO9/4pxxVb9HgzPPcvQ0uTN28OW4c9UOzLBAiItnOh4NTzHLEMDY7Hx5Fkcu3yEIb1WHjkzUjOv6wbBhAfBerOYeAU8IGKP3qhGKuhEyEij8EgT0YD0RDSEcZ7dP1bE1rKPSHxCbeA45B+nLEccYwWeFMH+ZepnIEEBLNqvHgnFYCdABCIj8qXQ4PmWsgOOrDgIQeJ+hWi2BZIm4RNjb3z0oQNps4JX8/H/ggkIFCEibC/jF4eI8VywnEZhgFy8QmzWNggb9OT80857o5MMPh3vugsPepe74eQCTNmEP54FZJwQyn0fehQoxcsPXH/lIFnPZ2IYKy/kYkI+YcOOKpqdmfMJ5ybXaTxXVjHO3ih7VUCF8Tm9I5jW3m18kyzM+N541NyLGIHFN5jACtPt0J51w5SH9o7IzlkUJsKIZsJ5NjprKwYS7/Rh3ChpourA8+nErDvVQCVx2wsBhsThnPh/SAgsaldDobN0e0Ge3zl66iKZZ26sTEXV4OeMggx8EGyePvChHc5JKwKwn7WylWQVEgraesrl8BwIvwfY4I+dOlBos136UEY78IQ3y5vgksLNwownJG99eEMshzSNfpkBnteMIyGERw9b5gkXKwqo+NketFxvB7gnzCilsV5znQ6v7rV2fm+MtNx2U96nbGjmctmYMjqdmff1UxdV7DVYPTvZDNtGl4GsuJZhO45JWznS+VBNzIUG2hUCf3S0DOH/dLKBeEw3rL1eFPCd5UAj2HsqmKvj4A0SZrnG0ggXvHVd3ji2KRvwJJi+d2BcyNV+k2n30x6sukJbRz4hsonJnMFgS3M7StA4V11CShe/ukw1CkcWGxy5qp9YDwWzjS0pSE5wbKINddBKyvrQk8eBLB4r8BFFnG528H9pTHXCYgBKxnj8Q5Hz3wGzoQ+LIzKYus03hHQsatZB2mBNy74QP5opHNBVuCn5E6J6E7hGWAyjE+8I2xebquc1Ndc9pxGvSpMUL2QdfYPLvS2ydhr6nxLWXGW6DqqtivvX2LPU2gA74MLGS+DYLtWvFeEO9QpRWlCUgX6mpSvSAV3vFdBC2nOQyslEyFFaVhIUbqQSwFdo2S8qIQJWNUXNKPLgSw1Nf9fz5wAjavMCcNGZbih3nKsuP5HJE8YtjK3sycstTnDdQ+fPuHB6CobzH6vYvoE+l1kuKDhEynKeKtjXMifKB0yZDAYSWNiqQOS35hAgTG+gcyLGOTgXEigKOMTKHC3BBMooFR8bx38xH3uDvhQ+WtVBaujuoeaZ4vaNmZYpbNZM2/poAtMG3Ech16o7qPvCS5meIISB/hA6L/5n2R6UulJT5xVTqeg/TsijPN3MWxMepiQ1jTghZgQGCGfQEnKc7YJvpLy3c7gxWG3Mygvz/JwB9F3I+co+l7q5h0hUXZEMN/P3O7e8C+Rq/uAP3jv/gaRCF6L9fex4LqwC1eJhPP1AeFEYK2oSuCaCFpgFzJyFpgF2UcpzzVgWFkf+3u9i9Onzk1IV3kji4krEcdxlB6F9SQXnzCX5J9YeuLpSaQnmZ5UetL+KScVp4VQpHx2z0N2KwgjJMtZ8tgmnh8KGt5sTq30yc8TIQcEAf9a2ZAkBA8SBU2HltXg+PkIHGQCxcr0Be5gl2TTrvrm6NBn5C8FiU6E/4NIYBBYX9TI16Y7+sI7dBP94liAC/9Hpzc0N0sZrnArcJB9rQsJB437kXDchaRlfgOYYaM0+KVNFlYxAYsQvU7geXAlYxMLN3YSZoWu6GZjo3+ZD57za0XrfnMK/3ySBMwft77dIV93zgUWrXvOaV0RNxwB5MvOOcuCdJad/P/SWW64yHLGRZZ3ushyzkWW7+ciyzkXWf4/4yJj0y837WSn7xbGFg4LFk85SGNDTI2fyngZTpfh4a/+kxk//bRadb+vPoYvaIBlQBCrD79/G7+V8eH3Fbf+JzBaH37/9u0bgIQcSz7ZzE72c/hDMUdSc6TyP2k5zhXlXK2IuUr6n4wY58piqtXEVGv8Ty7bpoYrCEO5VWp+vtX4WaBsHh/nrYTws0UgrRSRxBrXyNf1C27KPRAohGUCDVgf/mXhXx7+DfRlOvwbsODhPQ/L8DCOh3E8EIi7gH1YV4T3MsCT4Xcb1omEtTYncLdyAZ4L41yA58J6Lh5IZMC0kbQTFrea9pQ2xVzcdSQuj4PjxljcEYtbYnFP4HeEh3Q2cbqI04UbT3yz3Q774RS+rTWelRg5deUmh7sqVrjgB5DHuZqSkLTbeHLxRPrqZGSxvxFOiSNcuyEYSmfzHh626Q9jZJOVHHeoAmAd/jUBITPdelp0d9m9DPvfio3L2Y2nqRUipFKYx8qv9vkCf45rXM6R+jCyaWTjxE2RZRJbSZ4B2O/z5Tmbl+H6aEd+LriuPEdGMUxkCBtQtBMpGRm/WL5Qu44icNIUlGYI/1cBvAqb1OH/OvzfxAOKaIaDshX6NelrjcFG1UcfzlTUi+M6PHx/LBgCvH+CK8c1ixXwj2xkx2MySbFqqgzK04mrhD9TtCstoBr5ZyUDeBXIoMO/ZgojLPoM39XffKrwE6RFzkwizh0KZMBhISgzauGkT12L2s/PpQbgnBI/KLmN805DKVTkEfUin3W8lpxNqo5kcmRLgOJyp592xWok7lEf13qbFattfiulmjT3tZpnUXksJXuhQE1PoV2hG8XZ5SdxKT0USr3ZHPzLcNo8ljxoHTUxeg99tgD+AZ1cwkQmYXKZhH0qEJCUe5c4QZU6TrKa1DmFPw2fjxWPKsrYEiuEr3Jn7DUuEI98nmkn7kJ09GTFhoT7UKvx5NCFecmRS3yY/LMpQyaXJJog3jJsn44PhRBQXBEwDvACtBg/BC0YkA04VqRpSiJlbgGr7QDfNr1UH1MdMR2FyCPXUL+fyq2JG1Y12qd4DLURnzoiLXO0DV8oLrmd1FQVC5GsUuMWWYI45hKXinSQJSTQyTXkNvvW8jiZjOui/58USIoHVLHmvrSFlDKMOy3s0HZTbYSU8WIjw344X3abEjLF6kmUSuGqlpqqC0av2MKlljyWx61TXVPEmnGpQ+VLm0xhNdYohXgSrsaNR1CBxTgrQZ52z8OlpKWkRKtk78ka4S8jZQsVKrehcXNB+7I5PJZHAO06C7yZ7e4EjF2QTt21hf1QzobWGWp6fmL+k/BZ1Jod+uhLJ5menmFY5/hQ2joyqeSieu3jNvp43H1iqaQ6chr5D//mzqfIz6ZS24Q3mit1XLE8rhknoL3C06ddSUAyQZVHsGFe4Ylq8sAKI14uMOFXnfNrI9JguliEtmaZRg6C1zx2/+3tXImRB29yyDDvOk2aQOmAOoDIFKIg5OF82RxKF7Qn7WrMY9lqhfiHBPKsXbZ9PysJQfg3JseiEovJM1M6FLUPJcP/VXRWwr8mBhqE1RpD3KQuk5g0DnoeTNr7ZbMrMwnkAeShYJhY6noo9lC2T03mlpbKUXLnUr6iWuJ0LLMqmnau7WTmvsKczGK/QVtFQHU+mGdBYprTFK1Gxo3Cc0mia3t8Luy7oANtQu/7ZU5D5UtySuVRErNQUl4vCR7R6wm/R55vmXKI4paT512hMvG0x/RScipL3xsebInM+VITrvQubDlvyhMPm8umNPLkfnJxeRiG54IKtE8yd/APw+fNdV+qWkXJ7RKGfBg+10YQvlJEuKwx7giEl32+0PNweKjOmpH8nWuyh91LSVEqfQFd0/msOiuXnaQMSkOT53g9VQl/RurOPG0ybLZfSv4hT7Lprgz74amikiG9jox5h8N283yexJDQF7Ig2Bn+81oFOpw85HzatdLSZGYpZTViUjhmJTIZGF6GUxl8U3YuRY0x2J6LHodf/d8pRWTP8EeRMtJmrrRHsQUornMZToeSTNyQxZCcTp93p3NxpBQn5cFWmFqa/kyPj+F90GpyuoGwRuk3KZIlZKlRb7pBRdYvWdcifZ8dxDRwcxn145qTHEUzVqisVh2F1jm1aM3qRAkrN5KAixqJZEqmiTFW7fDn//7lcnk+f/j+++Gw/rr7Zfc8POw26+Pp8Xv43/dZKPtxShKSn+4Fcxi+Xo6HP3Fz/rjffP14/Pzx6v/kx2b/8fG0edldJp49XAEgBE5X0NtpnuwvGv0eadXmx20Zg+usYMJM48Drg62r47eSpVFUqqTpsozY5xP+jZKMUIsSnmUuJCvBN8sVucNSlbUDFulPXmX6i6yD8huEqJyoWIGo2wQSrrHcz2tceZLFVIJPyY+MZ0tT2mfFNtK9fhwKnUlbMlsebmpJ6KNe6iM9+yzAfRwuU0ZUeTaMSkOnjVI55Mfh8rT59QJ/j3Uf/h5rnm/KcyL9lNvTGucvx6+X8Pd78zpaHvb3VLonzL+mP9CZY5CLW08VImEF/0em8ygw09mOPLndy1DwKemDJk3eR35KoVqfiuuZOcw0V+Ww9KTrUXfI2Hq50unIjdJUg4VlwvTrrvQwKRZVhYvtJ5feKSfbPAqvdve5DIrJmDwFxUkGptFxkgrZPoQAMvyd95INKPew5djFFT6iC/bxabh8OVZcRYWnLjv7KgtCOYdEwrqulVSVwklgGy1DjCplvq829WJ02ScFNOkPGe3LtLuprAbHPW83lfBKOqAsJjZThmTlXuZTd1Br2P3X1EEwZAG4OKfna8ktnPJqyjptnH4eTpdSNsiMYyEbmGes/Ck6VoxH04qj4U8kPk5Tn5b0j6rwpmxXrMOdotJadryM4PEvNJahHLmXXKXtDi/HX0qlTZbtF1Xfyiq+JT0OkR9lIyO0RCMRimiC0fN+81sVnsOXJjKjRpnmXWmMNclZC7pBdpeqkDRXHExThn2hwxlZBcr7+36p8kOK5IVG5emX3WEAVbAthepGKTMzVb9cL5eqyZVS2K1G3fBXGTPY2VEliYhsIadHlwY1+KKaFgA+fXooEWakCnoXDzmHOwnq4M8PjFF7Y3uC1WtUeQqW1SgDZjSv+BXoshF82HxMhFGCUq1S5nmYIPPJ+VYmvi+jOyuTMphY0WaebL8r3T+yravleu2Pj5UwGFKe8rN52vxSqmRB+7WinFa1QpHiw/Nphyorx+kCZG4CnjbnAlxmPlcNjzaKUqN0/7S5VOlMRRf/Wsf0tPl193R9KtUkmeOozj4sMZT9G2T6V+dzahfTkZcgyI7hlmX2C9/KvVB+riWrnvxeEKfr4XH408D/ZPs/OYEKOgAsOc1S+7bmXpDn4akMdxx1kO7u7bzgXz//mPld372EP4z+0b8ruZCs4PNMHT8da9Wn5tNrWStpne+Yrlu6RWT3dU6K6/6yey77ejWpQTKH5zBsh/N5U2bHOV0cHBNJLF/ka6lLblQEU1I3/FtXBmNb+b0Vwihn9cWSacUwdSwmY1/6p4WQymKnl+NhV/Xf92Q1QedTf61CF7I/q/ILRnuVN/YdjiXNydPO9fehavjhdMNKOe30VNkLMqHrinlVezBFqNw4Ha5PnyqfQFLAUhdu00Qcrk/DabetCyukncl5+vjpP4Ztgf1Mt3uAHfm27rON51kVMW/1006vN0xjvNmiwbFM6SzKatzRS1uXSKrSSJ2kTmSpb4tUmfObCeMquV0r1JikTWHh2I2c6vdpC0kVldr4xk2wpBqSYKb+4hQ0ZBxdWgpNKteltaUb/do3a05Jn1DFp4h2eTmDkT5p0sjxCO5pnMk9sqoNi3TIUjduU+6Pp4dagVDuUR5UHC9fqmlkJnC8ntWyh8+ba5VZY0VivtVJ2ufz99VVELK9Kd6sijeq4k0q25er1RkNRievipnHS8UCpJPeur37fDzvJhcjs1aJRjkucx2fT8N5OL1UVQKyjJmf5PNpeNkdr6W/SiYoxaRK1ujmKRF7aXTz5JUPZsp50yD4+XR8Hk6XqmmbTmdGYcnROL7sHqorWeRVrkpn32rGqvr7ydRbMk2tbNppqCop5L2DyD1KlrMvu6fhZXe+Qs64FumsICxnWiC1mluyvmCeX2ptXCSK/QqmwHN7PD1MWnooQdGNq+ppCWKvWYEyCtiUoxqLVLvLUhERfGuZz8NpqJLdlPr8Q8vSJ2jrOtWkyNJVihKu04DKo2oJ4+TdDpYDDVOrtMoNuVx69SrKXaDUtPg9qXk37qgXnPOf192pVAIziZQcy7jA+XnYTu9i85k7Qi2bexrOVXsgnfgqEt/j9KfKZsrcZjZaqOKPYYHL9VSFZWRxYva+cr5eZXzoPHw+8wi2YSZJmt0SioFPg4XLVSopIMtFNucNSOSU0yjz0sqt43RM/8g6Z2zz+5YNK24zl+5cd7iQhbOc/OftZl/dKaGm5QHneTuUAS7LvO2VCuduKL1xHkqYhgyq8wOfZq/I2LhRwobZsYzdaiSEPz4w9kU0xMCJfLFD6Xkycgt5yH2ukmA9mX5Ljv2sG3+e71IpbhlRnR5nukulKMdR5vA816WSd173VDPZudGlwrPrJYEarZlfjtd91dpMZQ+o7NaNbzTA3so90ZXs8qzG+WX9nrxvnjUk5Hy7e4K+sTJ5mvvAqX1LTuOdtMSkrdjm/VuxUFZ3TFA3Xmcr56JabT52WlJpn9rqSdxQ6LSw3Va7HdfFZckbFKvDOTqlIKbw60a/DLAMllmFf3XYj4mZqFbXEvH5oPSxj/iRj+j81bfJ3/h5oEXV/xSuTz8QMrnHP/O9oKr/sErzjPRd1xE8J5O9pd7c1R8P4TNtuy037HzYPJ+/HKsmFzpzmbPHVNdRKqWkfZh+2ZS9NSxvvb0lbLHdpsHydaRdNCm2buVGhox5R6rcXucVqXv1je9PJbd/+iGqySc4GsmEJYlEXpD2Mvy3MiAhs9F518D5UvZ2OLKTUbRpVSf2bpVaWjJTbOT4XLJIZk1vKYx4dbkMyBdO8uB/O1+G0rla2G8dd9UoZPtFyy+qkA2Xmaxfqoo/Y5SnNhemXL6UX2Ah2+gmbdvFTapGFj3F6JPseV6wvHwpa46LyBnYf0mNgfqWXZSgqlufrDnUqfSqxLi4u//O5vbYIjL5Ala8IBfVYmTfeFEutpZEdRnzBcSXsepayKQEQBnXqvf0Vcb1VrPvjQbWdMRT7Trzdb9UuJna75kP/830yCYn+y4tnco9LYfg8qX+ThHZMkFYlrjOaRjW/1GuRRZ8W9/ChKRnYQjIr6YQBqBM0M58ks3WFM71/6WoWVMWbL7Nuq2Lp1XXW91383dUM9G69dWjaSg8cumMczmt3CZ+S9zVZCoIiEvfknTvml1PrYiazHNUwp1W8FmpsliXBW7l0dQeVszyznlt1JwSgUk0k+cMBf3NGVbp7SR/eW61hFJKn8hzEPUHvIgWhck3CatjDZm+qt5FCWpFrmVku1YpVvpmyPSCUxb7V8nxnmxRytlu8iUEMiRKlz1ldhrlhRhOt1fnQFvZH1dcu29UjiYXOQijnzTEzQsdE3v4ShOXLNtddmyx+SppVgYcXNCB6ETLpU/l5hwe151+NoabIvlB3RqLC1QfDyG/3lEkDMolpjqQqTwUCXTR8d+YOKaSldfnklb0zfyco58fNpdS3OkP4GRhTN1cQN/iz7M/13MFisZxVRraqfM2cuSE7WQJsuqnIOsn9XflquikFOqKBciusntjniKyuZ6rLBvZk1qnKaMOlpWnEX2nVAGOHkd0xm+lNxGvl6H+WhF5zaMRUsU1fANssUr+ldqpUrQFiRJy5Xq+n7YqYPBcsiKJK5Wt8rWmPbmk6mljsyu/k0J/VjrnqpfdeWKiTG7lJVUxj/X2aaWPNFJ/dFyY+jLuuktYOSaVAab6MCgzXocMZeNDSibj2l+H3eOXS30hmEw/9vXMqjM9q/m5RtKo9U3Zr1+Guv2Lk+1fLfdo9qbr1y+78rYD2Ty/6OLc111pAuhvhdh8UlWHI9shgvqK+4gIhH/v/Y4T1TJ081421Vb09Xj6pfRNSAd0eSPit2/wxzKed8/DHr7p9+Gnn799+z9vpXH0bOYAAA=="; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/docs/api/classes/BodyCenterTransformation.html b/docs/api/classes/BodyCenterTransformation.html index 4de5ad9..f1829ed 100644 --- a/docs/api/classes/BodyCenterTransformation.html +++ b/docs/api/classes/BodyCenterTransformation.html @@ -1,7 +1,10 @@ -BodyCenterTransformation | nbody

Class BodyCenterTransformation

Frame of reference transformation to the center of the first body in the system.

-

Implements

Constructors

constructor +BodyCenterTransformation | nbody

Class BodyCenterTransformation

Frame of reference transformation to the center of body i in the system.

+

Implements

Constructors

Properties

Methods

Constructors

Methods

  • Transform the frame of reference to the center of the first body in the system.

    +

Constructors

Properties

index: number

Methods

Generated using TypeDoc

\ No newline at end of file +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/api/classes/CelestialBody.html b/docs/api/classes/CelestialBody.html index 53d96dc..1a1d305 100644 --- a/docs/api/classes/CelestialBody.html +++ b/docs/api/classes/CelestialBody.html @@ -1,5 +1,5 @@ CelestialBody | nbody

Class CelestialBody

Represents a celestial body with all of its kinematic properties.

-

Constructors

Constructors

Properties

acceleration label mass @@ -12,14 +12,14 @@
  • position: Vector3

    position of the body.

  • velocity: Vector3

    velocity of the body.

  • acceleration: Vector3

    acceleration of the body.

    -
  • Returns CelestialBody

    Properties

    acceleration: Vector3

    Acceleration vector of the body.

    -
    label: string

    Label of the body.

    -
    mass: number

    Mean mass of the body.

    -
    position: Vector3

    Position vector of the body.

    -
    velocity: Vector3

    Velocity vector of the body.

    -

    Methods

    • Deep copy the current CelestialBody with the updated kinematic properties.

      +

    Returns CelestialBody

    Properties

    acceleration: Vector3

    Acceleration vector of the body.

    +
    label: string

    Label of the body.

    +
    mass: number

    Mean mass of the body.

    +
    position: Vector3

    Position vector of the body.

    +
    velocity: Vector3

    Velocity vector of the body.

    +

    Methods

    • Deep copy the current CelestialBody with the updated kinematic properties.

      Parameters

      • Optional position: Vector3

        new position.

      • Optional velocity: Vector3

        new velocity.

      • Optional acceleration: Vector3

        new acceleration.

      Returns CelestialBody

      a new CelestialBody instance with the updated properties.

      -

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/CentripetalForce.html b/docs/api/classes/CentripetalForce.html index ed3b0e9..bc61615 100644 --- a/docs/api/classes/CentripetalForce.html +++ b/docs/api/classes/CentripetalForce.html @@ -1,11 +1,11 @@ CentripetalForce | nbody

    Class CentripetalForce

    Represents a Centripetal force object. To be used to calculate the force required to keep the bodies in circular motion around a given center.

    -

    Implements

    Constructors

    Implements

    Constructors

    Properties

    Methods

    Constructors

    Properties

    center: Vector3

    Center of force.

    -

    Methods

    • Calculate the force required to keep the bodies in circular motion around the center. arr[i] represents the centripetal force required for the ith body.

      +

    Returns CentripetalForce

    Properties

    center: Vector3

    Center of force.

    +

    Methods

    • Calculate the force required to keep the bodies in circular motion around the center. arr[i] represents the centripetal force required for the ith body.

      Parameters

      Returns Vector3[]

      forces.

      -

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/CoMTransformation.html b/docs/api/classes/CoMTransformation.html index e26c520..07520b0 100644 --- a/docs/api/classes/CoMTransformation.html +++ b/docs/api/classes/CoMTransformation.html @@ -1,7 +1,7 @@ CoMTransformation | nbody

    Class CoMTransformation

    Frame of reference transformation to the center of mass of the system.

    -

    Implements

    Constructors

    Implements

    Constructors

    Methods

    Constructors

    Methods

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/CombinedForce.html b/docs/api/classes/CombinedForce.html index e554df8..49b95bc 100644 --- a/docs/api/classes/CombinedForce.html +++ b/docs/api/classes/CombinedForce.html @@ -1,10 +1,10 @@ CombinedForce | nbody

    Class CombinedForce

    Represents a combined force object. To be used to additively combine multiple forces acting on a system of bodies.

    -

    Implements

    Constructors

    Implements

    Constructors

    Properties

    Methods

    Constructors

    Properties

    forces: Force[]

    Methods

    • Get the combined forces acting on the bodies. arr[i] represents the combined force acting on the ith body as a result of all force systems.

      +

    Returns CombinedForce

    Properties

    forces: Force[]

    Methods

    • Get the combined forces acting on the bodies. arr[i] represents the combined force acting on the ith body as a result of all force systems.

      Parameters

      Returns Vector3[]

      element-wise combined forces.

      -

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/ExplicitEulerSim.html b/docs/api/classes/ExplicitEulerSim.html index 6f82eb6..08a919c 100644 --- a/docs/api/classes/ExplicitEulerSim.html +++ b/docs/api/classes/ExplicitEulerSim.html @@ -1,12 +1,12 @@ ExplicitEulerSim | nbody

    Class ExplicitEulerSim

    Represents a simulation function object that uses the Euler integration method to simulate motions of bodies.

    -

    Implements

    Constructors

    Implements

    Constructors

    Properties

    Methods

    Constructors

    Properties

    force: Force

    Force object to calculate forces on bodies in the Universe.

    -

    Methods

    • Simulate a step in the Universe by using the current state and a time step, using the Euler integration method.

      +

    Constructors

    Properties

    force: Force

    Force object to calculate forces on bodies in the Universe.

    +

    Methods

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/Gravity.html b/docs/api/classes/Gravity.html index 7690a53..808f0e1 100644 --- a/docs/api/classes/Gravity.html +++ b/docs/api/classes/Gravity.html @@ -1,13 +1,13 @@ Gravity | nbody

    Class Gravity

    Represents a Newtonian Gravitational force object.

    -

    Implements

    Constructors

    Implements

    Constructors

    Properties

    G

    Methods

    Constructors

    • Create a new Gravity with the provided gravitational constant.

      Parameters

      • G: number = 6.674e-11

        gravitational constant.

        -

      Returns Gravity

    Properties

    G: number

    Gravitational constant.

    +

    Returns Gravity

    Properties

    G: number

    Gravitational constant.

    Default Value

    6.674e-11
     
    -

    Methods

    • Calculate and return the forces acting on the bodies. arr[i] represents the force acting on the ith body as a result of all other bodies.

      +

    Methods

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/LambdaForce.html b/docs/api/classes/LambdaForce.html index 9af3673..340ecfc 100644 --- a/docs/api/classes/LambdaForce.html +++ b/docs/api/classes/LambdaForce.html @@ -1,5 +1,5 @@ LambdaForce | nbody

    Class LambdaForce

    Function object that uses the user-defined lambda function to calculate the forces acting on the bodies.

    -

    Implements

    Constructors

    Implements

    Constructors

    Properties

    Methods

    Constructors

    • Create a new LambdaForce with the provided lambda function.

      @@ -9,8 +9,8 @@
    • Length of the returned array should be equal to the length of the input array of CelestialBodies.

    Parameters

    Returns LambdaForce

    Properties

    fn: ((bodies) => Vector3[])

    Lambda function to calculate forces, provided by the user.

    -

    Type declaration

    Methods

    Returns LambdaForce

    Properties

    fn: ((bodies) => Vector3[])

    Lambda function to calculate forces, provided by the user.

    +

    Type declaration

    Methods

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/LambdaSim.html b/docs/api/classes/LambdaSim.html index 13e714e..3943622 100644 --- a/docs/api/classes/LambdaSim.html +++ b/docs/api/classes/LambdaSim.html @@ -1,5 +1,5 @@ LambdaSim | nbody

    Class LambdaSim

    Function object that uses the user-defined lambda function to simulate the Universe.

    -

    Implements

    Constructors

    Implements

    Constructors

    Properties

    Methods

    Constructors

    • Create a new LambdaSim with the provided lambda function.

      @@ -8,9 +8,9 @@
    • The lambda function should call or calculate the forces action on the bodies by itself.

    Parameters

    • fn: ((deltaT, currState, prevState) => State)

      lambda function.

      -
        • (deltaT, currState, prevState): State
        • Parameters

          Returns State

    Returns LambdaSim

    Properties

    fn: ((deltaT, currState, prevState) => State)

    Type declaration

      • (deltaT, currState, prevState): State
      • Parameters

        Returns State

    Methods

    • Simulate the Universe using the lambda function.

      +
        • (deltaT, currState, prevState): State
        • Parameters

          Returns State

    Returns LambdaSim

    Properties

    fn: ((deltaT, currState, prevState) => State)

    Type declaration

      • (deltaT, currState, prevState): State
      • Parameters

        Returns State

    Methods

    • Simulate the Universe using the lambda function.

      Parameters

      • deltaT: number

        time step.

      • currState: State

        current state of the Universe.

      • prevState: State

        previous state of the Universe.

      Returns State

      the next state of the Universe.

      -

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/LambdaTransformation.html b/docs/api/classes/LambdaTransformation.html index 78eaae0..0c39256 100644 --- a/docs/api/classes/LambdaTransformation.html +++ b/docs/api/classes/LambdaTransformation.html @@ -1,5 +1,5 @@ LambdaTransformation | nbody

    Class LambdaTransformation

    A Frame of Reference transformation that uses the user-defined lambda function.

    -

    Implements

    Constructors

    Implements

    Constructors

    Properties

    Methods

    Constructors

    • Create a new LambdaTransformer with the provided lambda function.

      @@ -9,8 +9,8 @@
    • Transformed state should contain the same number of bodies as the input state, and the order should be preserved.

    Parameters

    • fn: ((state, deltaT) => State)

      lambda function.

      -

    Returns LambdaTransformation

    Properties

    fn: ((state, deltaT) => State)

    Type declaration

    Methods

    • Transform the state's frame of reference using the lambda function.

      +

    Returns LambdaTransformation

    Properties

    fn: ((state, deltaT) => State)

    Type declaration

    Methods

    • Transform the state's frame of reference using the lambda function.

      Parameters

      • state: State

        state to transform.

      • deltaT: number

        time step taken to get to this state. Only applicable for time-dependent transformations.

      Returns State

      transformed state.

      -

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/PinTransformation.html b/docs/api/classes/PinTransformation.html new file mode 100644 index 0000000..b2e80ff --- /dev/null +++ b/docs/api/classes/PinTransformation.html @@ -0,0 +1,12 @@ +PinTransformation | nbody

    Class PinTransformation

    Frame of reference transformation to a pin body i to the given axis.

    +

    Implements

    Constructors

    Properties

    Methods

    Constructors

    Properties

    axis: Vector3
    index: number

    Methods

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/RealTimeVisualizer.html b/docs/api/classes/RealTimeVisualizer.html index 1ab304a..8c3b89c 100644 --- a/docs/api/classes/RealTimeVisualizer.html +++ b/docs/api/classes/RealTimeVisualizer.html @@ -1,5 +1,5 @@ RealTimeVisualizer | nbody

    Class RealTimeVisualizer

    2D real-time visualizer using Plotly.

    -

    Implements

    • Visualizer

    Constructors

    Implements

    • Visualizer

    Constructors

    Properties

    animationId divId simulation @@ -9,11 +9,11 @@ stop

    Constructors

    Properties

    animationId: null | number = null
    divId: string = ''
    simulation: Simulation
    universeTrails: PlotlyUniverseTrail[] = []

    Methods

    • Adds default controls using lil-gui to the visualization.

      +

    Returns RealTimeVisualizer

    Properties

    animationId: null | number = null
    divId: string = ''
    simulation: Simulation
    universeTrails: PlotlyUniverseTrail[] = []

    Methods

    • Adds default controls using lil-gui to the visualization.

      Parameters

      • parentElement: HTMLElement

        parent element to place the controller div in.

        -

      Returns void

    • Simulate and play the visualization.

      +

    Returns void

    • Simulate and play the visualization.

      Parameters

      • divId: string

        div id to render the visualization in.

      • width: number

        width of the visualization.

      • height: number

        height of the visualization.

        -

      Returns void

    Generated using TypeDoc

    \ No newline at end of file +

    Returns void

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/RealTimeVisualizer3D.html b/docs/api/classes/RealTimeVisualizer3D.html index a02bc6c..8951654 100644 --- a/docs/api/classes/RealTimeVisualizer3D.html +++ b/docs/api/classes/RealTimeVisualizer3D.html @@ -1,7 +1,7 @@ RealTimeVisualizer3D | nbody

    Class RealTimeVisualizer3D

    3D real-time visualizer using Three.js.

    -

    Implements

    • Visualizer

    Constructors

    Implements

    • Visualizer

    Constructors

    Properties

    Constructors

    Properties

    animationId: null | number = null
    renderer: null | WebGLRenderer = null
    scene?: Scene
    simulation: Simulation
    universeTrails: ThreeUniverseTrail[] = []

    Methods

    • Adds default controls to the visualization.

      +

    Returns RealTimeVisualizer3D

    Properties

    animationId: null | number = null
    clear: (() => void) = ...

    Clear the visualization.

    +

    Type declaration

      • (): void
      • Returns void

    scene?: Scene
    simulation: Simulation
    universeTrails: ThreeUniverseTrail[] = []

    Methods

    • Adds default controls to the visualization.

      Parameters

      • parentElement: HTMLElement

        parent element to place the controller div in.

        -

      Returns void

    • Simulate and play the visualization

      +

    Returns void

    • Simulate and play the visualization

      Parameters

      • divId: string

        div id to render the visualization in

      • width: number

        width of the visualization.

      • height: number

        height of the visualization.

        -

      Returns void

    Generated using TypeDoc

    \ No newline at end of file +

    Returns void

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/RecordingVisualizer.html b/docs/api/classes/RecordingVisualizer.html index 1708d3d..96de0ab 100644 --- a/docs/api/classes/RecordingVisualizer.html +++ b/docs/api/classes/RecordingVisualizer.html @@ -1,5 +1,5 @@ RecordingVisualizer | nbody

    Class RecordingVisualizer

    2D recording visualizer using Plotly.

    -

    Implements

    • Visualizer

    Constructors

    Implements

    • Visualizer

    Constructors

    Properties

    animationId divId simulation @@ -9,12 +9,12 @@ stop

    Constructors

    Properties

    animationId: null | number = null
    divId: string = ''
    simulation: Simulation
    universeTrails: PlotlyUniverseTrail[] = []

    Methods

    • Adds default controls using lil-gui to the visualization.

      +

    Returns RecordingVisualizer

    Properties

    animationId: null | number = null
    divId: string = ''
    simulation: Simulation
    universeTrails: PlotlyUniverseTrail[] = []

    Methods

    • Adds default controls using lil-gui to the visualization.

      Parameters

      • parentElement: HTMLElement

        parent element to place the controller div in.

        -

      Returns void

    • Simulate and play the visualization.

      +

    Returns void

    • Simulate and play the visualization.

      Parameters

      • divId: string

        div id to render the visualization in.

      • width: number

        width of the visualization.

      • height: number

        height of the visualization.

      • recordFor: number

        number of seconds to record for..

        -

      Returns void

    Generated using TypeDoc

    \ No newline at end of file +

    Returns void

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/RecordingVisualizer3D.html b/docs/api/classes/RecordingVisualizer3D.html index dacce09..37ea4eb 100644 --- a/docs/api/classes/RecordingVisualizer3D.html +++ b/docs/api/classes/RecordingVisualizer3D.html @@ -1,6 +1,7 @@ RecordingVisualizer3D | nbody

    Class RecordingVisualizer3D

    3D recording visualizer using Three.js.

    -

    Implements

    • Visualizer

    Constructors

    Implements

    • Visualizer

    Constructors

    Properties

    Constructors

    Properties

    animationId: null | number = null
    scene?: Scene
    simulation: Simulation
    universeTrails: ThreeUniverseTrail[] = []

    Methods

    • Adds default controls to the visualization.

      +

    Returns RecordingVisualizer3D

    Properties

    animationId: null | number = null
    clear: (() => void) = ...

    Clear the visualization.

    +

    Type declaration

      • (): void
      • Returns void

    scene?: Scene
    simulation: Simulation
    universeTrails: ThreeUniverseTrail[] = []

    Methods

    • Adds default controls to the visualization.

      Parameters

      • parentElement: HTMLElement

        parent element to place the controller div in.

        -

      Returns void

    • Simulate and play the visualization

      +

    Returns void

    • Simulate and play the visualization

      Parameters

      • divId: string

        div id to render the visualization in.

      • width: number

        width of the visualization.

      • height: number

        height of the visualization.

      • recordFor: number

        number of seconds to record for.

        -

      Returns void

    Generated using TypeDoc

    \ No newline at end of file +

    Returns void

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/RotateTransformation.html b/docs/api/classes/RotateTransformation.html index 15930b1..b4f84ed 100644 --- a/docs/api/classes/RotateTransformation.html +++ b/docs/api/classes/RotateTransformation.html @@ -1,12 +1,12 @@ RotateTransformation | nbody

    Class RotateTransformation

    Frame of reference transformation around an axis by an angle. Makes sense to this transformation only during initialization of the universe and not at every time step.

    -

    Implements

    Constructors

    Implements

    Constructors

    Properties

    Methods

    Constructors

    Properties

    angle: number
    axis: Vector3

    Methods

    • Transform the frame of reference around an axis by an angle.

      +

    Returns RotateTransformation

    Properties

    angle: number
    axis: Vector3

    Methods

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/RungeKutta4Sim.html b/docs/api/classes/RungeKutta4Sim.html index 5bd41fb..35c101f 100644 --- a/docs/api/classes/RungeKutta4Sim.html +++ b/docs/api/classes/RungeKutta4Sim.html @@ -1,15 +1,15 @@ RungeKutta4Sim | nbody

    Class RungeKutta4Sim

    Represents a simulation function object that uses the Runge-Kutta 4 integration method to simulate the motion of bodies.

    -

    Implements

    Constructors

    Implements

    Constructors

    Properties

    Methods

    Constructors

    Properties

    force: Force

    Force object to calculate forces on bodies in the Universe.

    -
    weights: number[]

    Weights for weighted average.

    -

    Methods

    • Simulate a step in the Universe by using the current state and a time step, using the Runge-Kutta 4 integration method.

      +

    Constructors

    • Create a new RungeKutta4Sim with the provided weights for average force calculator, which is invoked on every simulation step.

      +

      Parameters

      • force: Force = ...

        force calculator.

        +
      • weights: number[] = ...

        weights for weighted average.

        +

      Returns RungeKutta4Sim

    Properties

    force: Force

    Force object to calculate forces on bodies in the Universe.

    +
    weights: number[]

    Weights for weighted average.

    +

    Methods

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/SemiImplicitEulerSim.html b/docs/api/classes/SemiImplicitEulerSim.html index 6486eaa..676f831 100644 --- a/docs/api/classes/SemiImplicitEulerSim.html +++ b/docs/api/classes/SemiImplicitEulerSim.html @@ -1,12 +1,12 @@ SemiImplicitEulerSim | nbody

    Class SemiImplicitEulerSim

    Represents a simulation function object that uses the Semi-Implicit Euler integration method to simulate the motion of bodies.

    -

    Implements

    Constructors

    Implements

    Constructors

    Properties

    Methods

    Constructors

    Properties

    force: Force

    Force object to calculate forces on bodies in the Universe.

    -

    Methods

    • Simulate a step in the Universe by using the current state and a time step, using the Semi-Implicit Euler integration method.

      +

    Constructors

    Properties

    force: Force

    Force object to calculate forces on bodies in the Universe.

    +

    Methods

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/Simulation.html b/docs/api/classes/Simulation.html index c7608d0..9170315 100644 --- a/docs/api/classes/Simulation.html +++ b/docs/api/classes/Simulation.html @@ -1,5 +1,5 @@ Simulation | nbody

    Class Simulation

    A Simulation object that contains Universes and a Visualizer.

    -

    Constructors

    Constructors

    Methods

    Constructors

    • Create a new Simulation object with the provided Universes and visualization config.

      Parameters

      • universes: Universe | Universe[]

        array of Universes.

        -
      • __namedParameters: {
            controller?: ControllerType;
            looped?: boolean;
            maxFrameRate?: number;
            maxTrailLength?: number;
            record?: boolean;
            showDebugInfo?: boolean;
            showTrails?: boolean;
            visType?: VisType;
        }
        • Optional controller?: ControllerType
        • Optional looped?: boolean
        • Optional maxFrameRate?: number
        • Optional maxTrailLength?: number
        • Optional record?: boolean
        • Optional showDebugInfo?: boolean
        • Optional showTrails?: boolean
        • Optional visType?: VisType

      Returns Simulation

    Methods

    • Get the maximum trail length used in the visualization.

      +
    • __namedParameters: {
          controller?: ControllerType;
          looped?: boolean;
          maxFrameRate?: number;
          maxTrailLength?: number;
          record?: boolean;
          showDebugInfo?: boolean;
          showTrails?: boolean;
          visType?: VisType;
      }
      • Optional controller?: ControllerType
      • Optional looped?: boolean
      • Optional maxFrameRate?: number
      • Optional maxTrailLength?: number
      • Optional record?: boolean
      • Optional showDebugInfo?: boolean
      • Optional showTrails?: boolean
      • Optional visType?: VisType

    Returns Simulation

    Methods

    • Get the maximum trail length used in the visualization.

      Returns number

      maximum trail length.

      -
    • Get whether trails are shown in the visualization.

      +
    • Get whether trails are shown in the visualization.

      Returns boolean

      true if trails are shown.

      -
    • True if the universe with the given label is shown.

      +
    • True if the universe with the given label is shown.

      Parameters

      • label: string

        universe label.

      Returns boolean

      whether the universe is shown.

      -
    • Get the speed of the simulation.

      Returns number

      speed of the simulation as a scale of normal time.

      -
    • Get whether the simulation is playing.

      Returns boolean

      true if the simulation is playing.

      -
    • Pause the simulation. Only works if the controller is 'code'.

      -

      Returns void

    • Resume the simulation. Only works if the controller is 'code'.

      -

      Returns void

    • Set the maximum trail length used in the visualization. Changes only apply on the next Simulation.play() call.

      +
    • Pause the simulation. Only works if the controller is 'code'.

      +

      Returns void

    • Resume the simulation. Only works if the controller is 'code'.

      +

      Returns void

    • Set the maximum trail length used in the visualization. Changes only apply on the next Simulation.play() call.

      Parameters

      • maxTrailLength: number

        maximum trail length.

        -

      Returns void

    • Set whether to show trails in the visualization. Only works if the controller is 'code'.

      +

    Returns void

    • Set whether to show trails in the visualization. Only works if the controller is 'code'.

      Parameters

      • showTrails: boolean

        true to show trails.

        -

      Returns void

    • Set whether to show the universe with the given label. Only works if the controller is 'code'.

      +

    Returns void

    • Set whether to show the universe with the given label. Only works if the controller is 'code'.

      Parameters

      • label: string

        universe label.

      • show: boolean

        true to show the universe.

        -

      Returns void

    • Set the speed of the simulation. Only works if the controller is 'code'.

      +

    Returns void

    • Set the speed of the simulation. Only works if the controller is 'code'.

      Parameters

      • speed: number

        speed of the simulation as a scale of normal time.

        -

      Returns void

    • Insert the simulation visualization in the div with the given id.

      +

    Returns void

    • Insert the simulation visualization in the div with the given id.

      Parameters

      • divId: string

        div id.

      • width: number
      • height: number
      • speed: number = 1

        initial time scale.

      • paused: boolean = false

        whether to start the simulation paused.

      • recordFor: number = 0

        number of seconds to record for, only used if in record mode.

        -

      Returns void

    Generated using TypeDoc

    \ No newline at end of file +

    Returns void

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/State.html b/docs/api/classes/State.html index 959d744..3a9cf51 100644 --- a/docs/api/classes/State.html +++ b/docs/api/classes/State.html @@ -1,10 +1,10 @@ State | nbody

    Class State

    Represents a Universe's state snapshot.

    -

    Constructors

    Constructors

    Properties

    Methods

    Constructors

    Properties

    bodies: CelestialBody[]

    Array of celestial bodies that make up this state of the Universe.

    -

    Methods

    • Deep copy this state

      +

    Returns State

    Properties

    bodies: CelestialBody[]

    Array of celestial bodies that make up this state of the Universe.

    +

    Methods

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/TimedRotateTransformation.html b/docs/api/classes/TimedRotateTransformation.html new file mode 100644 index 0000000..e915006 --- /dev/null +++ b/docs/api/classes/TimedRotateTransformation.html @@ -0,0 +1,13 @@ +TimedRotateTransformation | nbody

    Class TimedRotateTransformation

    Frame of reference transformation to rotate around an axis by 360 degrees in a given time.

    +

    Implements

    Constructors

    Properties

    Methods

    Constructors

    Properties

    axis: Vector3
    revolutionTime: number

    Methods

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/Universe.html b/docs/api/classes/Universe.html index 2165480..73aeadc 100644 --- a/docs/api/classes/Universe.html +++ b/docs/api/classes/Universe.html @@ -1,5 +1,5 @@ Universe | nbody

    Class Universe

    A Universe object that contains previous and current state of the universe, a simulation function, frame of reference transformations and other necessary data.

    -

    Constructors

    Constructors

    Properties

    color currState label @@ -9,10 +9,10 @@

    Methods

    Constructors

    Properties

    color: string | string[]

    Color of the bodies in the Universe. A single color applied to all bodies or an array of colors applied to each body respectively. Incase of array, length should match the number of bodies in the state.

    -
    currState: State
    label: string

    Label of the Universe.

    -
    prevState: State

    Simulation function used to simulate the Universe.

    -
    transformations: Transformation[]

    Array of transformations to be applied to the Universe's state after simulation and before visualization.

    -

    Methods

    • Deep copy the current Universe.

      +

    Returns Universe

    Properties

    color: string | string[]

    Color of the bodies in the Universe. A single color applied to all bodies or an array of colors applied to each body respectively. Incase of array, length should match the number of bodies in the state.

    +
    currState: State
    label: string

    Label of the Universe.

    +
    prevState: State

    Simulation function used to simulate the Universe.

    +
    transformations: Transformation[]

    Array of transformations to be applied to the Universe's state after simulation and before visualization.

    +

    Methods

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/classes/VelocityVerletSim.html b/docs/api/classes/VelocityVerletSim.html index eaed144..af5ea4f 100644 --- a/docs/api/classes/VelocityVerletSim.html +++ b/docs/api/classes/VelocityVerletSim.html @@ -1,12 +1,12 @@ VelocityVerletSim | nbody

    Class VelocityVerletSim

    Represents a simulation function object that uses the Velocity Verlet integration method to simulate the motion of bodies.

    -

    Implements

    Constructors

    Implements

    Constructors

    Properties

    Methods

    Constructors

    Properties

    forceCalculator: Force

    Force object to calculate forces on bodies in the Universe.

    -

    Methods

    • Simulate a step in the Universe by using the previous and/or current state and a time step, using the Velocity Verlet integration method.

      +

    Constructors

    Properties

    forceCalculator: Force

    Force object to calculate forces on bodies in the Universe.

    +

    Methods

    • Simulate a step in the Universe by using the previous and/or current state and a time step, using the Velocity Verlet integration method.

      Parameters

      • deltaT: number

        time step.

      • currState: State

        current state.

      Returns State

      new state after the simulation step.

      -

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/hierarchy.html b/docs/api/hierarchy.html index 502d9dd..92baa30 100644 --- a/docs/api/hierarchy.html +++ b/docs/api/hierarchy.html @@ -1 +1 @@ -nbody

    Generated using TypeDoc

    \ No newline at end of file +nbody

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/interfaces/Force.html b/docs/api/interfaces/Force.html index 3a73c81..9c0af5e 100644 --- a/docs/api/interfaces/Force.html +++ b/docs/api/interfaces/Force.html @@ -1,3 +1,3 @@ Force | nbody

    Interface Force

    Represents a force object used to calculate forces acting on the bodies in the Universe.

    -
    interface Force {
        getForces(bodies): Vector3[];
    }

    Implemented by

    Methods

    Methods

    Generated using TypeDoc

    \ No newline at end of file +
    interface Force {
        getForces(bodies): Vector3[];
    }

    Implemented by

    Methods

    Methods

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/interfaces/SimulateFunction.html b/docs/api/interfaces/SimulateFunction.html index b6c3eb1..fd76e2e 100644 --- a/docs/api/interfaces/SimulateFunction.html +++ b/docs/api/interfaces/SimulateFunction.html @@ -1,8 +1,8 @@ SimulateFunction | nbody

    Interface SimulateFunction

    Represents a function object used for simulating the Universe. Should encapsulate the numerical integration method and other necessary simulation logic. Can use an external force calculation function object - see Force.

    -
    interface SimulateFunction {
        simulate(deltaT, currState, prevState): State;
    }

    Implemented by

    Methods

    interface SimulateFunction {
        simulate(deltaT, currState, prevState): State;
    }

    Implemented by

    Methods

    Methods

    • Simulate a step in the Universe by using the previous and/or current state and a time step.

      Parameters

      • deltaT: number

        time step.

      • currState: State

        current state of the Universe.

      • prevState: State

        previous state of the Universe.

      Returns State

      the next state of the Universe.

      -

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/interfaces/Transformation.html b/docs/api/interfaces/Transformation.html index 7188acb..69962e3 100644 --- a/docs/api/interfaces/Transformation.html +++ b/docs/api/interfaces/Transformation.html @@ -1,7 +1,7 @@ Transformation | nbody

    Interface Transformation

    Represents a Frame of Reference transformation.

    -
    interface Transformation {
        transform(state, deltaT): State;
    }

    Implemented by

    Methods

    interface Transformation {
        transform(state, deltaT): State;
    }

    Implemented by

    Methods

    Methods

    • Transform the state to a new frame of reference.

      Parameters

      • state: State

        state to transform.

      • deltaT: number

        time step taken to get to this state. Only applicable for time-dependent transformations.

      Returns State

      transformed state.

      -

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/modules.html b/docs/api/modules.html index 2c8467f..184935e 100644 --- a/docs/api/modules.html +++ b/docs/api/modules.html @@ -1,6 +1,8 @@ nbody

    nbody

    Index

    Building blocks

    Interfaces

    Force diff --git a/docs/api/types/ControllerType.html b/docs/api/types/ControllerType.html index cade469..c48b7f7 100644 --- a/docs/api/types/ControllerType.html +++ b/docs/api/types/ControllerType.html @@ -4,4 +4,4 @@
  • 'code' for manual control via code.
  • 'none' for no control.
  • -

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/types/UniverseConfig.html b/docs/api/types/UniverseConfig.html index aadcccd..2a42f35 100644 --- a/docs/api/types/UniverseConfig.html +++ b/docs/api/types/UniverseConfig.html @@ -5,4 +5,4 @@
  • prevState: State

    Previous state of the Universe.

  • simFunc: SimulateFunction

    Simulation function used to simulate the Universe.

  • transformations: Transformation[]

    Array of transformations to be applied to the Universe's state after simulation and before visualization.

    -
  • Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/api/types/VisType.html b/docs/api/types/VisType.html index fa55c58..a480533 100644 --- a/docs/api/types/VisType.html +++ b/docs/api/types/VisType.html @@ -1,2 +1,2 @@ VisType | nbody

    Type alias VisType

    VisType: "2D" | "3D"

    Visualization type.

    -

    Generated using TypeDoc

    \ No newline at end of file +

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/assets/54509 YORP-OMNIj1fz.js b/docs/assets/54509 YORP-OMNIj1fz.js new file mode 100644 index 0000000..51d91da --- /dev/null +++ b/docs/assets/54509 YORP-OMNIj1fz.js @@ -0,0 +1,4 @@ +import{j as o}from"./jsx-runtime-DRTy3Uxn.js";import{useMDXComponents as e}from"./index-z5U8iC57.js";import{M as i,d as s}from"./index-iQvBBB1Z.js";import{YORP as m}from"./HorseshoeOrbit.stories-BMFTqkxu.js";import"./index-BBkUAzwr.js";import"./iframe-C3OM9s_A.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";import"./Simulation-7H4WISkI.js";import"./Transformation-KUb0IKcR.js";function n(t){const r={h1:"h1",p:"p",...e(),...t.components};return o.jsxs(o.Fragment,{children:[o.jsx(i,{title:"Showcase/HorseshoeOrbit/54509 YORP"}),` +`,o.jsx(r.h1,{id:"54509-yorp",children:"54509 YORP"}),` +`,o.jsx(r.p,{children:"54509 YORP is an Earth co-orbital asteroid that follows a horse shoe orbit"}),` +`,o.jsx(s,{of:m})]})}function R(t={}){const{wrapper:r}={...e(),...t.components};return r?o.jsx(r,{...t,children:o.jsx(n,{...t})}):n(t)}export{R as default}; diff --git a/docs/assets/Analemma-Iy1rkUoS.js b/docs/assets/Analemma-Iy1rkUoS.js new file mode 100644 index 0000000..c168655 --- /dev/null +++ b/docs/assets/Analemma-Iy1rkUoS.js @@ -0,0 +1,2 @@ +import{j as n}from"./jsx-runtime-DRTy3Uxn.js";import{useMDXComponents as m}from"./index-z5U8iC57.js";import{M as r}from"./index-iQvBBB1Z.js";import"./index-BBkUAzwr.js";import"./iframe-C3OM9s_A.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";function e(t){const o={h1:"h1",...m(),...t.components};return n.jsxs(n.Fragment,{children:[n.jsx(r,{title:"Showcase/Analemma/Intro"}),` +`,n.jsx(o.h1,{id:"analemma",children:"Analemma"})]})}function j(t={}){const{wrapper:o}={...m(),...t.components};return o?n.jsx(o,{...t,children:n.jsx(e,{...t})}):e(t)}export{j as default}; diff --git a/docs/assets/Analemma.stories-CHKHhkqr.js b/docs/assets/Analemma.stories-CHKHhkqr.js new file mode 100644 index 0000000..d69b39a --- /dev/null +++ b/docs/assets/Analemma.stories-CHKHhkqr.js @@ -0,0 +1,13 @@ +import{C as o,V as e,U as i,a as l,R as u,S as c}from"./Simulation-7H4WISkI.js";import{R as m,T as w,B as h}from"./Transformation-KUb0IKcR.js";import"./jsx-runtime-DRTy3Uxn.js";import"./index-BBkUAzwr.js";const p=new o("Sun",1989e27,new e(0,0,0),new e(0,0,0),new e(0,0,0)),d=new o("Earth",5972e21,new e(1521e8,0,0),new e(0,0,-29290),new e(0,0,0)),T=new m(new e(0,0,1),-(23.4/180)*Math.PI),S=new m(new e(0,1,0),-(Math.PI/2)),a=new h(1),f=new i({label:"Sun-Earth System",currState:S.transform(T.transform(a.transform(new l([p.clone(),d.clone()])))),color:["#FDB813","#287AB8"],simFunc:new u,transformations:[a,new w(new e(0,1,0),365.25*24*60*60)]}),R={title:"Showcase/Analemma",component:c,parameters:{layout:"centered",controls:{disable:!0}},tags:[],argTypes:{},args:{}},n={args:{storyName:"SunEarthAnalemma",universe:[f],controller:"ui",showTrails:!0,speed:5e6,visType:"3D",width:800,maxTrailLength:300,showDebugInfo:!0}};var t,r,s;n.parameters={...n.parameters,docs:{...(t=n.parameters)==null?void 0:t.docs,source:{originalSource:`{ + args: { + storyName: "SunEarthAnalemma", + universe: [sunEarth], + controller: 'ui', + showTrails: true, + speed: 5000000, + visType: '3D', + width: 800, + maxTrailLength: 300, + showDebugInfo: true + } +}`,...(s=(r=n.parameters)==null?void 0:r.docs)==null?void 0:s.source}}};const b=["SunEarthAnalemma"];export{n as SunEarthAnalemma,b as __namedExportsOrder,R as default}; diff --git a/docs/assets/CelestialBody-CpuBovvJ.js b/docs/assets/CelestialBody-CpuBovvJ.js deleted file mode 100644 index 3d48f91..0000000 --- a/docs/assets/CelestialBody-CpuBovvJ.js +++ /dev/null @@ -1,9 +0,0 @@ -import{j as e}from"./jsx-runtime-DRTy3Uxn.js";import{useMDXComponents as r}from"./index-z5U8iC57.js";import{M as s}from"./index-Bd-WH8Nz.js";import"./index-BBkUAzwr.js";import"./iframe-CQxbU3Lp.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";function o(n){const t={code:"code",pre:"pre",...r(),...n.components};return e.jsxs(e.Fragment,{children:[e.jsx(s,{title:"Usage/CelestialBody"}),` -`,e.jsx(t.pre,{children:e.jsx(t.code,{className:"language-ts",children:` const a = new CelestialBody(\r - "a",\r - 1,\r - new Vector3(-0.97000436, 0.24308753, 0),\r - new Vector3(0.466203685, 0.43236573, 0),\r - new Vector3(0, 0, 0)\r - ); -`})})]})}function j(n={}){const{wrapper:t}={...r(),...n.components};return t?e.jsx(t,{...n,children:e.jsx(o,{...n})}):o(n)}export{j as default}; diff --git a/docs/assets/Color-RQJUDNI5-D9WPpOf3.js b/docs/assets/Color-RQJUDNI5-roFcWcLK.js similarity index 99% rename from docs/assets/Color-RQJUDNI5-D9WPpOf3.js rename to docs/assets/Color-RQJUDNI5-roFcWcLK.js index c1aaa31..a49fccb 100644 --- a/docs/assets/Color-RQJUDNI5-D9WPpOf3.js +++ b/docs/assets/Color-RQJUDNI5-roFcWcLK.js @@ -1 +1 @@ -import{n as M,e as ue,T as Me,F as Ce,f as $e,g as Ne}from"./index-Bd-WH8Nz.js";import{R as h,r as b,g as fe}from"./index-BBkUAzwr.js";import{_ as Oe,i as J,a as Ie}from"./index-DrlA5mbP.js";import"./iframe-CQxbU3Lp.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrFu-skq.js";function $(){return($=Object.assign||function(e){for(var t=1;t=0||(o[n]=e[n]);return o}function K(e){var t=b.useRef(e),n=b.useRef(function(r){t.current&&t.current(r)});return t.current=e,n.current}var S=function(e,t,n){return t===void 0&&(t=0),n===void 0&&(n=1),e>n?n:e0:p.buttons>0)&&o.current?a(ne(o.current,p,l.current)):_(!1)},N=function(){return _(!1)};function _(p){var m=i.current,x=V(o.current),C=p?x.addEventListener:x.removeEventListener;C(m?"touchmove":"mousemove",k),C(m?"touchend":"mouseup",N)}return[function(p){var m=p.nativeEvent,x=o.current;if(x&&(re(m),!function(X,R){return R&&!j(X)}(m,i.current)&&x)){if(j(m)){i.current=!0;var C=m.changedTouches||[];C.length&&(l.current=C[0].identifier)}x.focus(),a(ne(x,m,l.current)),_(!0)}},function(p){var m=p.which||p.keyCode;m<37||m>40||(p.preventDefault(),s({left:m===39?.05:m===37?-.05:0,top:m===40?.05:m===38?-.05:0}))},_]},[s,a]),d=c[0],f=c[1],g=c[2];return b.useEffect(function(){return g},[g]),h.createElement("div",$({},r,{onTouchStart:d,onMouseDown:d,className:"react-colorful__interactive",ref:o,onKeyDown:f,tabIndex:0,role:"slider"}))}),z=function(e){return e.filter(Boolean).join(" ")},ee=function(e){var t=e.color,n=e.left,r=e.top,o=r===void 0?.5:r,a=z(["react-colorful__pointer",e.className]);return h.createElement("div",{className:a,style:{top:100*o+"%",left:100*n+"%"}},h.createElement("div",{className:"react-colorful__pointer-fill",style:{backgroundColor:t}}))},y=function(e,t,n){return t===void 0&&(t=0),n===void 0&&(n=Math.pow(10,t)),Math.round(n*e)/n},Se={grad:.9,turn:360,rad:360/(2*Math.PI)},Re=function(e){return ge(A(e))},A=function(e){return e[0]==="#"&&(e=e.substring(1)),e.length<6?{r:parseInt(e[0]+e[0],16),g:parseInt(e[1]+e[1],16),b:parseInt(e[2]+e[2],16),a:e.length===4?y(parseInt(e[3]+e[3],16)/255,2):1}:{r:parseInt(e.substring(0,2),16),g:parseInt(e.substring(2,4),16),b:parseInt(e.substring(4,6),16),a:e.length===8?y(parseInt(e.substring(6,8),16)/255,2):1}},Te=function(e,t){return t===void 0&&(t="deg"),Number(e)*(Se[t]||1)},je=function(e){var t=/hsla?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i.exec(e);return t?Fe({h:Te(t[1],t[2]),s:Number(t[3]),l:Number(t[4]),a:t[5]===void 0?1:Number(t[5])/(t[6]?100:1)}):{h:0,s:0,v:0,a:1}},Fe=function(e){var t=e.s,n=e.l;return{h:e.h,s:(t*=(n<50?n:100-n)/100)>0?2*t/(n+t)*100:0,v:n+t,a:e.a}},ze=function(e){return Pe(de(e))},he=function(e){var t=e.s,n=e.v,r=e.a,o=(200-t)*n/100;return{h:y(e.h),s:y(o>0&&o<200?t*n/100/(o<=100?o:200-o)*100:0),l:y(o/2),a:y(r,2)}},G=function(e){var t=he(e);return"hsl("+t.h+", "+t.s+"%, "+t.l+"%)"},q=function(e){var t=he(e);return"hsla("+t.h+", "+t.s+"%, "+t.l+"%, "+t.a+")"},de=function(e){var t=e.h,n=e.s,r=e.v,o=e.a;t=t/360*6,n/=100,r/=100;var a=Math.floor(t),s=r*(1-n),l=r*(1-(t-a)*n),i=r*(1-(1-t+a)*n),c=a%6;return{r:y(255*[r,l,s,s,i,r][c]),g:y(255*[i,r,r,l,s,s][c]),b:y(255*[s,s,i,r,r,l][c]),a:y(o,2)}},He=function(e){var t=/rgba?\(?\s*(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i.exec(e);return t?ge({r:Number(t[1])/(t[2]?100/255:1),g:Number(t[3])/(t[4]?100/255:1),b:Number(t[5])/(t[6]?100/255:1),a:t[7]===void 0?1:Number(t[7])/(t[8]?100:1)}):{h:0,s:0,v:0,a:1}},H=function(e){var t=e.toString(16);return t.length<2?"0"+t:t},Pe=function(e){var t=e.r,n=e.g,r=e.b,o=e.a,a=o<1?H(y(255*o)):"";return"#"+H(t)+H(n)+H(r)+a},ge=function(e){var t=e.r,n=e.g,r=e.b,o=e.a,a=Math.max(t,n,r),s=a-Math.min(t,n,r),l=s?a===t?(n-r)/s:a===n?2+(r-t)/s:4+(t-n)/s:0;return{h:y(60*(l<0?l+6:l)),s:y(a?s/a*100:0),v:y(a/255*100),a:o}},me=h.memo(function(e){var t=e.hue,n=e.onChange,r=z(["react-colorful__hue",e.className]);return h.createElement("div",{className:r},h.createElement(Z,{onMove:function(o){n({h:360*o.left})},onKey:function(o){n({h:S(t+360*o.left,0,360)})},"aria-label":"Hue","aria-valuenow":y(t),"aria-valuemax":"360","aria-valuemin":"0"},h.createElement(ee,{className:"react-colorful__hue-pointer",left:t/360,color:G({h:t,s:100,v:100,a:1})})))}),be=h.memo(function(e){var t=e.hsva,n=e.onChange,r={backgroundColor:G({h:t.h,s:100,v:100,a:1})};return h.createElement("div",{className:"react-colorful__saturation",style:r},h.createElement(Z,{onMove:function(o){n({s:100*o.left,v:100-100*o.top})},onKey:function(o){n({s:S(t.s+100*o.left,0,100),v:S(t.v-100*o.top,0,100)})},"aria-label":"Color","aria-valuetext":"Saturation "+y(t.s)+"%, Brightness "+y(t.v)+"%"},h.createElement(ee,{className:"react-colorful__saturation-pointer",top:1-t.v/100,left:t.s/100,color:G(t)})))}),ve=function(e,t){if(e===t)return!0;for(var n in e)if(e[n]!==t[n])return!1;return!0},pe=function(e,t){return e.replace(/\s/g,"")===t.replace(/\s/g,"")},Le=function(e,t){return e.toLowerCase()===t.toLowerCase()||ve(A(e),A(t))};function ye(e,t,n){var r=K(n),o=b.useState(function(){return e.toHsva(t)}),a=o[0],s=o[1],l=b.useRef({color:t,hsva:a});b.useEffect(function(){if(!e.equal(t,l.current.color)){var c=e.toHsva(t);l.current={hsva:c,color:t},s(c)}},[t,e]),b.useEffect(function(){var c;ve(a,l.current.hsva)||e.equal(c=e.fromHsva(a),l.current.color)||(l.current={hsva:a,color:c},r(c))},[a,e,r]);var i=b.useCallback(function(c){s(function(d){return Object.assign({},d,c)})},[]);return[a,i]}var qe=typeof window<"u"?b.useLayoutEffect:b.useEffect,Be=function(){return typeof __webpack_nonce__<"u"?__webpack_nonce__:void 0},oe=new Map,xe=function(e){qe(function(){var t=e.current?e.current.ownerDocument:document;if(t!==void 0&&!oe.has(t)){var n=t.createElement("style");n.innerHTML=`.react-colorful{position:relative;display:flex;flex-direction:column;width:200px;height:200px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.react-colorful__saturation{position:relative;flex-grow:1;border-color:transparent;border-bottom:12px solid #000;border-radius:8px 8px 0 0;background-image:linear-gradient(0deg,#000,transparent),linear-gradient(90deg,#fff,hsla(0,0%,100%,0))}.react-colorful__alpha-gradient,.react-colorful__pointer-fill{content:"";position:absolute;left:0;top:0;right:0;bottom:0;pointer-events:none;border-radius:inherit}.react-colorful__alpha-gradient,.react-colorful__saturation{box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}.react-colorful__alpha,.react-colorful__hue{position:relative;height:24px}.react-colorful__hue{background:linear-gradient(90deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red)}.react-colorful__last-control{border-radius:0 0 8px 8px}.react-colorful__interactive{position:absolute;left:0;top:0;right:0;bottom:0;border-radius:inherit;outline:none;touch-action:none}.react-colorful__pointer{position:absolute;z-index:1;box-sizing:border-box;width:28px;height:28px;transform:translate(-50%,-50%);background-color:#fff;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2)}.react-colorful__interactive:focus .react-colorful__pointer{transform:translate(-50%,-50%) scale(1.1)}.react-colorful__alpha,.react-colorful__alpha-pointer{background-color:#fff;background-image:url('data:image/svg+xml;charset=utf-8,')}.react-colorful__saturation-pointer{z-index:3}.react-colorful__hue-pointer{z-index:2}`,oe.set(t,n);var r=Be();r&&n.setAttribute("nonce",r),t.head.appendChild(n)}},[])},We=function(e){var t=e.className,n=e.colorModel,r=e.color,o=r===void 0?n.defaultColor:r,a=e.onChange,s=Q(e,["className","colorModel","color","onChange"]),l=b.useRef(null);xe(l);var i=ye(n,o,a),c=i[0],d=i[1],f=z(["react-colorful",t]);return h.createElement("div",$({},s,{ref:l,className:f}),h.createElement(be,{hsva:c,onChange:d}),h.createElement(me,{hue:c.h,onChange:d,className:"react-colorful__last-control"}))},Xe={defaultColor:"000",toHsva:Re,fromHsva:function(e){return ze({h:e.h,s:e.s,v:e.v,a:1})},equal:Le},De=function(e){return h.createElement(We,$({},e,{colorModel:Xe}))},Ke=function(e){var t=e.className,n=e.hsva,r=e.onChange,o={backgroundImage:"linear-gradient(90deg, "+q(Object.assign({},n,{a:0}))+", "+q(Object.assign({},n,{a:1}))+")"},a=z(["react-colorful__alpha",t]),s=y(100*n.a);return h.createElement("div",{className:a},h.createElement("div",{className:"react-colorful__alpha-gradient",style:o}),h.createElement(Z,{onMove:function(l){r({a:l.left})},onKey:function(l){r({a:S(n.a+l.left)})},"aria-label":"Alpha","aria-valuetext":s+"%","aria-valuenow":s,"aria-valuemin":"0","aria-valuemax":"100"},h.createElement(ee,{className:"react-colorful__alpha-pointer",left:n.a,color:q(n)})))},we=function(e){var t=e.className,n=e.colorModel,r=e.color,o=r===void 0?n.defaultColor:r,a=e.onChange,s=Q(e,["className","colorModel","color","onChange"]),l=b.useRef(null);xe(l);var i=ye(n,o,a),c=i[0],d=i[1],f=z(["react-colorful",t]);return h.createElement("div",$({},s,{ref:l,className:f}),h.createElement(be,{hsva:c,onChange:d}),h.createElement(me,{hue:c.h,onChange:d}),h.createElement(Ke,{hsva:c,onChange:d,className:"react-colorful__last-control"}))},Ve={defaultColor:"hsla(0, 0%, 0%, 1)",toHsva:je,fromHsva:q,equal:pe},Ae=function(e){return h.createElement(we,$({},e,{colorModel:Ve}))},Ge={defaultColor:"rgba(0, 0, 0, 1)",toHsva:He,fromHsva:function(e){var t=de(e);return"rgba("+t.r+", "+t.g+", "+t.b+", "+t.a+")"},equal:pe},Ue=function(e){return h.createElement(we,$({},e,{colorModel:Ge}))},Ye={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]};const F=Ye,ke={};for(const e of Object.keys(F))ke[F[e]]=e;const u={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};var _e=u;for(const e of Object.keys(u)){if(!("channels"in u[e]))throw new Error("missing channels property: "+e);if(!("labels"in u[e]))throw new Error("missing channel labels property: "+e);if(u[e].labels.length!==u[e].channels)throw new Error("channel and label counts mismatch: "+e);const{channels:t,labels:n}=u[e];delete u[e].channels,delete u[e].labels,Object.defineProperty(u[e],"channels",{value:t}),Object.defineProperty(u[e],"labels",{value:n})}u.rgb.hsl=function(e){const t=e[0]/255,n=e[1]/255,r=e[2]/255,o=Math.min(t,n,r),a=Math.max(t,n,r),s=a-o;let l,i;a===o?l=0:t===a?l=(n-r)/s:n===a?l=2+(r-t)/s:r===a&&(l=4+(t-n)/s),l=Math.min(l*60,360),l<0&&(l+=360);const c=(o+a)/2;return a===o?i=0:c<=.5?i=s/(a+o):i=s/(2-a-o),[l,i*100,c*100]};u.rgb.hsv=function(e){let t,n,r,o,a;const s=e[0]/255,l=e[1]/255,i=e[2]/255,c=Math.max(s,l,i),d=c-Math.min(s,l,i),f=function(g){return(c-g)/6/d+1/2};return d===0?(o=0,a=0):(a=d/c,t=f(s),n=f(l),r=f(i),s===c?o=r-n:l===c?o=1/3+t-r:i===c&&(o=2/3+n-t),o<0?o+=1:o>1&&(o-=1)),[o*360,a*100,c*100]};u.rgb.hwb=function(e){const t=e[0],n=e[1];let r=e[2];const o=u.rgb.hsl(e)[0],a=1/255*Math.min(t,Math.min(n,r));return r=1-1/255*Math.max(t,Math.max(n,r)),[o,a*100,r*100]};u.rgb.cmyk=function(e){const t=e[0]/255,n=e[1]/255,r=e[2]/255,o=Math.min(1-t,1-n,1-r),a=(1-t-o)/(1-o)||0,s=(1-n-o)/(1-o)||0,l=(1-r-o)/(1-o)||0;return[a*100,s*100,l*100,o*100]};function Je(e,t){return(e[0]-t[0])**2+(e[1]-t[1])**2+(e[2]-t[2])**2}u.rgb.keyword=function(e){const t=ke[e];if(t)return t;let n=1/0,r;for(const o of Object.keys(F)){const a=F[o],s=Je(e,a);s.04045?((t+.055)/1.055)**2.4:t/12.92,n=n>.04045?((n+.055)/1.055)**2.4:n/12.92,r=r>.04045?((r+.055)/1.055)**2.4:r/12.92;const o=t*.4124+n*.3576+r*.1805,a=t*.2126+n*.7152+r*.0722,s=t*.0193+n*.1192+r*.9505;return[o*100,a*100,s*100]};u.rgb.lab=function(e){const t=u.rgb.xyz(e);let n=t[0],r=t[1],o=t[2];n/=95.047,r/=100,o/=108.883,n=n>.008856?n**(1/3):7.787*n+16/116,r=r>.008856?r**(1/3):7.787*r+16/116,o=o>.008856?o**(1/3):7.787*o+16/116;const a=116*r-16,s=500*(n-r),l=200*(r-o);return[a,s,l]};u.hsl.rgb=function(e){const t=e[0]/360,n=e[1]/100,r=e[2]/100;let o,a,s;if(n===0)return s=r*255,[s,s,s];r<.5?o=r*(1+n):o=r+n-r*n;const l=2*r-o,i=[0,0,0];for(let c=0;c<3;c++)a=t+1/3*-(c-1),a<0&&a++,a>1&&a--,6*a<1?s=l+(o-l)*6*a:2*a<1?s=o:3*a<2?s=l+(o-l)*(2/3-a)*6:s=l,i[c]=s*255;return i};u.hsl.hsv=function(e){const t=e[0];let n=e[1]/100,r=e[2]/100,o=n;const a=Math.max(r,.01);r*=2,n*=r<=1?r:2-r,o*=a<=1?a:2-a;const s=(r+n)/2,l=r===0?2*o/(a+o):2*n/(r+n);return[t,l*100,s*100]};u.hsv.rgb=function(e){const t=e[0]/60,n=e[1]/100;let r=e[2]/100;const o=Math.floor(t)%6,a=t-Math.floor(t),s=255*r*(1-n),l=255*r*(1-n*a),i=255*r*(1-n*(1-a));switch(r*=255,o){case 0:return[r,i,s];case 1:return[l,r,s];case 2:return[s,r,i];case 3:return[s,l,r];case 4:return[i,s,r];case 5:return[r,s,l]}};u.hsv.hsl=function(e){const t=e[0],n=e[1]/100,r=e[2]/100,o=Math.max(r,.01);let a,s;s=(2-n)*r;const l=(2-n)*o;return a=n*o,a/=l<=1?l:2-l,a=a||0,s/=2,[t,a*100,s*100]};u.hwb.rgb=function(e){const t=e[0]/360;let n=e[1]/100,r=e[2]/100;const o=n+r;let a;o>1&&(n/=o,r/=o);const s=Math.floor(6*t),l=1-r;a=6*t-s,s&1&&(a=1-a);const i=n+a*(l-n);let c,d,f;switch(s){default:case 6:case 0:c=l,d=i,f=n;break;case 1:c=i,d=l,f=n;break;case 2:c=n,d=l,f=i;break;case 3:c=n,d=i,f=l;break;case 4:c=i,d=n,f=l;break;case 5:c=l,d=n,f=i;break}return[c*255,d*255,f*255]};u.cmyk.rgb=function(e){const t=e[0]/100,n=e[1]/100,r=e[2]/100,o=e[3]/100,a=1-Math.min(1,t*(1-o)+o),s=1-Math.min(1,n*(1-o)+o),l=1-Math.min(1,r*(1-o)+o);return[a*255,s*255,l*255]};u.xyz.rgb=function(e){const t=e[0]/100,n=e[1]/100,r=e[2]/100;let o,a,s;return o=t*3.2406+n*-1.5372+r*-.4986,a=t*-.9689+n*1.8758+r*.0415,s=t*.0557+n*-.204+r*1.057,o=o>.0031308?1.055*o**(1/2.4)-.055:o*12.92,a=a>.0031308?1.055*a**(1/2.4)-.055:a*12.92,s=s>.0031308?1.055*s**(1/2.4)-.055:s*12.92,o=Math.min(Math.max(0,o),1),a=Math.min(Math.max(0,a),1),s=Math.min(Math.max(0,s),1),[o*255,a*255,s*255]};u.xyz.lab=function(e){let t=e[0],n=e[1],r=e[2];t/=95.047,n/=100,r/=108.883,t=t>.008856?t**(1/3):7.787*t+16/116,n=n>.008856?n**(1/3):7.787*n+16/116,r=r>.008856?r**(1/3):7.787*r+16/116;const o=116*n-16,a=500*(t-n),s=200*(n-r);return[o,a,s]};u.lab.xyz=function(e){const t=e[0],n=e[1],r=e[2];let o,a,s;a=(t+16)/116,o=n/500+a,s=a-r/200;const l=a**3,i=o**3,c=s**3;return a=l>.008856?l:(a-16/116)/7.787,o=i>.008856?i:(o-16/116)/7.787,s=c>.008856?c:(s-16/116)/7.787,o*=95.047,a*=100,s*=108.883,[o,a,s]};u.lab.lch=function(e){const t=e[0],n=e[1],r=e[2];let o;o=Math.atan2(r,n)*360/2/Math.PI,o<0&&(o+=360);const s=Math.sqrt(n*n+r*r);return[t,s,o]};u.lch.lab=function(e){const t=e[0],n=e[1],o=e[2]/360*2*Math.PI,a=n*Math.cos(o),s=n*Math.sin(o);return[t,a,s]};u.rgb.ansi16=function(e,t=null){const[n,r,o]=e;let a=t===null?u.rgb.hsv(e)[2]:t;if(a=Math.round(a/50),a===0)return 30;let s=30+(Math.round(o/255)<<2|Math.round(r/255)<<1|Math.round(n/255));return a===2&&(s+=60),s};u.hsv.ansi16=function(e){return u.rgb.ansi16(u.hsv.rgb(e),e[2])};u.rgb.ansi256=function(e){const t=e[0],n=e[1],r=e[2];return t===n&&n===r?t<8?16:t>248?231:Math.round((t-8)/247*24)+232:16+36*Math.round(t/255*5)+6*Math.round(n/255*5)+Math.round(r/255*5)};u.ansi16.rgb=function(e){let t=e%10;if(t===0||t===7)return e>50&&(t+=3.5),t=t/10.5*255,[t,t,t];const n=(~~(e>50)+1)*.5,r=(t&1)*n*255,o=(t>>1&1)*n*255,a=(t>>2&1)*n*255;return[r,o,a]};u.ansi256.rgb=function(e){if(e>=232){const a=(e-232)*10+8;return[a,a,a]}e-=16;let t;const n=Math.floor(e/36)/5*255,r=Math.floor((t=e%36)/6)/5*255,o=t%6/5*255;return[n,r,o]};u.rgb.hex=function(e){const n=(((Math.round(e[0])&255)<<16)+((Math.round(e[1])&255)<<8)+(Math.round(e[2])&255)).toString(16).toUpperCase();return"000000".substring(n.length)+n};u.hex.rgb=function(e){const t=e.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!t)return[0,0,0];let n=t[0];t[0].length===3&&(n=n.split("").map(l=>l+l).join(""));const r=parseInt(n,16),o=r>>16&255,a=r>>8&255,s=r&255;return[o,a,s]};u.rgb.hcg=function(e){const t=e[0]/255,n=e[1]/255,r=e[2]/255,o=Math.max(Math.max(t,n),r),a=Math.min(Math.min(t,n),r),s=o-a;let l,i;return s<1?l=a/(1-s):l=0,s<=0?i=0:o===t?i=(n-r)/s%6:o===n?i=2+(r-t)/s:i=4+(t-n)/s,i/=6,i%=1,[i*360,s*100,l*100]};u.hsl.hcg=function(e){const t=e[1]/100,n=e[2]/100,r=n<.5?2*t*n:2*t*(1-n);let o=0;return r<1&&(o=(n-.5*r)/(1-r)),[e[0],r*100,o*100]};u.hsv.hcg=function(e){const t=e[1]/100,n=e[2]/100,r=t*n;let o=0;return r<1&&(o=(n-r)/(1-r)),[e[0],r*100,o*100]};u.hcg.rgb=function(e){const t=e[0]/360,n=e[1]/100,r=e[2]/100;if(n===0)return[r*255,r*255,r*255];const o=[0,0,0],a=t%1*6,s=a%1,l=1-s;let i=0;switch(Math.floor(a)){case 0:o[0]=1,o[1]=s,o[2]=0;break;case 1:o[0]=l,o[1]=1,o[2]=0;break;case 2:o[0]=0,o[1]=1,o[2]=s;break;case 3:o[0]=0,o[1]=l,o[2]=1;break;case 4:o[0]=s,o[1]=0,o[2]=1;break;default:o[0]=1,o[1]=0,o[2]=l}return i=(1-n)*r,[(n*o[0]+i)*255,(n*o[1]+i)*255,(n*o[2]+i)*255]};u.hcg.hsv=function(e){const t=e[1]/100,n=e[2]/100,r=t+n*(1-t);let o=0;return r>0&&(o=t/r),[e[0],o*100,r*100]};u.hcg.hsl=function(e){const t=e[1]/100,r=e[2]/100*(1-t)+.5*t;let o=0;return r>0&&r<.5?o=t/(2*r):r>=.5&&r<1&&(o=t/(2*(1-r))),[e[0],o*100,r*100]};u.hcg.hwb=function(e){const t=e[1]/100,n=e[2]/100,r=t+n*(1-t);return[e[0],(r-t)*100,(1-r)*100]};u.hwb.hcg=function(e){const t=e[1]/100,r=1-e[2]/100,o=r-t;let a=0;return o<1&&(a=(r-o)/(1-o)),[e[0],o*100,a*100]};u.apple.rgb=function(e){return[e[0]/65535*255,e[1]/65535*255,e[2]/65535*255]};u.rgb.apple=function(e){return[e[0]/255*65535,e[1]/255*65535,e[2]/255*65535]};u.gray.rgb=function(e){return[e[0]/100*255,e[0]/100*255,e[0]/100*255]};u.gray.hsl=function(e){return[0,0,e[0]]};u.gray.hsv=u.gray.hsl;u.gray.hwb=function(e){return[0,100,e[0]]};u.gray.cmyk=function(e){return[0,0,0,e[0]]};u.gray.lab=function(e){return[e[0],0,0]};u.gray.hex=function(e){const t=Math.round(e[0]/100*255)&255,r=((t<<16)+(t<<8)+t).toString(16).toUpperCase();return"000000".substring(r.length)+r};u.rgb.gray=function(e){return[(e[0]+e[1]+e[2])/3/255*100]};const B=_e;function Qe(){const e={},t=Object.keys(B);for(let n=t.length,r=0;r1&&(n=r),e(n))};return"conversion"in e&&(t.conversion=e.conversion),t}function st(e){const t=function(...n){const r=n[0];if(r==null)return r;r.length>1&&(n=r);const o=e(n);if(typeof o=="object")for(let a=o.length,s=0;s{O[e]={},Object.defineProperty(O[e],"channels",{value:U[e].channels}),Object.defineProperty(O[e],"labels",{value:U[e].labels});const t=rt(e);Object.keys(t).forEach(r=>{const o=t[r];O[e][r]=st(o),O[e][r].raw=at(o)})});var lt=O;const w=fe(lt);var it=Oe,ct=function(){return it.Date.now()},ut=ct,ft=/\s/;function ht(e){for(var t=e.length;t--&&ft.test(e.charAt(t)););return t}var dt=ht,gt=dt,mt=/^\s+/;function bt(e){return e&&e.slice(0,gt(e)+1).replace(mt,"")}var vt=bt,pt=vt,ae=J,yt=Ie,se=NaN,xt=/^[-+]0x[0-9a-f]+$/i,wt=/^0b[01]+$/i,kt=/^0o[0-7]+$/i,_t=parseInt;function Et(e){if(typeof e=="number")return e;if(yt(e))return se;if(ae(e)){var t=typeof e.valueOf=="function"?e.valueOf():e;e=ae(t)?t+"":t}if(typeof e!="string")return e===0?e:+e;e=pt(e);var n=wt.test(e);return n||kt.test(e)?_t(e.slice(2),n?2:8):xt.test(e)?se:+e}var Mt=Et,Ct=J,D=ut,le=Mt,$t="Expected a function",Nt=Math.max,Ot=Math.min;function It(e,t,n){var r,o,a,s,l,i,c=0,d=!1,f=!1,g=!0;if(typeof e!="function")throw new TypeError($t);t=le(t)||0,Ct(n)&&(d=!!n.leading,f="maxWait"in n,a=f?Nt(le(n.maxWait)||0,t):a,g="trailing"in n?!!n.trailing:g);function k(v){var E=r,T=o;return r=o=void 0,c=v,s=e.apply(T,E),s}function N(v){return c=v,l=setTimeout(m,t),d?k(v):s}function _(v){var E=v-i,T=v-c,te=t-E;return f?Ot(te,a-T):te}function p(v){var E=v-i,T=v-c;return i===void 0||E>=t||E<0||f&&T>=a}function m(){var v=D();if(p(v))return x(v);l=setTimeout(m,_(v))}function x(v){return l=void 0,g&&r?k(v):(r=o=void 0,s)}function C(){l!==void 0&&clearTimeout(l),c=0,r=i=o=l=void 0}function X(){return l===void 0?s:x(D())}function R(){var v=D(),E=p(v);if(r=arguments,o=this,i=v,E){if(l===void 0)return N(i);if(f)return clearTimeout(l),l=setTimeout(m,t),k(i)}return l===void 0&&(l=setTimeout(m,t)),s}return R.cancel=C,R.flush=X,R}var St=It,Rt=St,Tt=J,jt="Expected a function";function Ft(e,t,n){var r=!0,o=!0;if(typeof e!="function")throw new TypeError(jt);return Tt(n)&&(r="leading"in n?!!n.leading:r,o="trailing"in n?!!n.trailing:o),Rt(e,t,{leading:r,maxWait:t,trailing:o})}var zt=Ft;const Ht=fe(zt);var Pt=M.div({position:"relative",maxWidth:250}),Lt=M(ue)({position:"absolute",zIndex:1,top:4,left:4}),qt=M.div({width:200,margin:5,".react-colorful__saturation":{borderRadius:"4px 4px 0 0"},".react-colorful__hue":{boxShadow:"inset 0 0 0 1px rgb(0 0 0 / 5%)"},".react-colorful__last-control":{borderRadius:"0 0 4px 4px"}}),Bt=M(Me)(({theme:e})=>({fontFamily:e.typography.fonts.base})),Wt=M.div({display:"grid",gridTemplateColumns:"repeat(9, 16px)",gap:6,padding:3,marginTop:5,width:200}),Xt=M.div(({theme:e,active:t})=>({width:16,height:16,boxShadow:t?`${e.appBorderColor} 0 0 0 1px inset, ${e.textMutedColor}50 0 0 0 4px`:`${e.appBorderColor} 0 0 0 1px inset`,borderRadius:e.appBorderRadius})),Dt=`url('data:image/svg+xml;charset=utf-8,')`,ie=({value:e,active:t,onClick:n,style:r,...o})=>{let a=`linear-gradient(${e}, ${e}), ${Dt}, linear-gradient(#fff, #fff)`;return h.createElement(Xt,{...o,active:t,onClick:n,style:{...r,backgroundImage:a}})},Kt=M(Ce.Input)(({theme:e})=>({width:"100%",paddingLeft:30,paddingRight:30,boxSizing:"border-box",fontFamily:e.typography.fonts.base})),Vt=M($e)(({theme:e})=>({position:"absolute",zIndex:1,top:6,right:7,width:20,height:20,padding:4,boxSizing:"border-box",cursor:"pointer",color:e.input.color})),Ee=(e=>(e.RGB="rgb",e.HSL="hsl",e.HEX="hex",e))(Ee||{}),P=Object.values(Ee),At=/\(([0-9]+),\s*([0-9]+)%?,\s*([0-9]+)%?,?\s*([0-9.]+)?\)/,Gt=/^\s*rgba?\(([0-9]+),\s*([0-9]+),\s*([0-9]+),?\s*([0-9.]+)?\)\s*$/i,Ut=/^\s*hsla?\(([0-9]+),\s*([0-9]+)%,\s*([0-9]+)%,?\s*([0-9.]+)?\)\s*$/i,Y=/^\s*#?([0-9a-f]{3}|[0-9a-f]{6})\s*$/i,Yt=/^\s*#?([0-9a-f]{3})\s*$/i,Jt={hex:De,rgb:Ue,hsl:Ae},L={hex:"transparent",rgb:"rgba(0, 0, 0, 0)",hsl:"hsla(0, 0%, 0%, 0)"},ce=e=>{let t=e==null?void 0:e.match(At);if(!t)return[0,0,0,1];let[,n,r,o,a=1]=t;return[n,r,o,a].map(Number)},I=e=>{if(!e)return;let t=!0;if(Gt.test(e)){let[s,l,i,c]=ce(e),[d,f,g]=w.rgb.hsl([s,l,i])||[0,0,0];return{valid:t,value:e,keyword:w.rgb.keyword([s,l,i]),colorSpace:"rgb",rgb:e,hsl:`hsla(${d}, ${f}%, ${g}%, ${c})`,hex:`#${w.rgb.hex([s,l,i]).toLowerCase()}`}}if(Ut.test(e)){let[s,l,i,c]=ce(e),[d,f,g]=w.hsl.rgb([s,l,i])||[0,0,0];return{valid:t,value:e,keyword:w.hsl.keyword([s,l,i]),colorSpace:"hsl",rgb:`rgba(${d}, ${f}, ${g}, ${c})`,hsl:e,hex:`#${w.hsl.hex([s,l,i]).toLowerCase()}`}}let n=e.replace("#",""),r=w.keyword.rgb(n)||w.hex.rgb(n),o=w.rgb.hsl(r),a=e;if(/[^#a-f0-9]/i.test(e)?a=n:Y.test(e)&&(a=`#${n}`),a.startsWith("#"))t=Y.test(a);else try{w.keyword.hex(a)}catch{t=!1}return{valid:t,value:a,keyword:w.rgb.keyword(r),colorSpace:"hex",rgb:`rgba(${r[0]}, ${r[1]}, ${r[2]}, 1)`,hsl:`hsla(${o[0]}, ${o[1]}%, ${o[2]}%, 1)`,hex:a}},Qt=(e,t,n)=>{if(!e||!(t!=null&&t.valid))return L[n];if(n!=="hex")return(t==null?void 0:t[n])||L[n];if(!t.hex.startsWith("#"))try{return`#${w.keyword.hex(t.hex)}`}catch{return L.hex}let r=t.hex.match(Yt);if(!r)return Y.test(t.hex)?t.hex:L.hex;let[o,a,s]=r[1].split("");return`#${o}${o}${a}${a}${s}${s}`},Zt=(e,t)=>{let[n,r]=b.useState(e||""),[o,a]=b.useState(()=>I(n)),[s,l]=b.useState((o==null?void 0:o.colorSpace)||"hex");b.useEffect(()=>{let f=e||"",g=I(f);r(f),a(g),l((g==null?void 0:g.colorSpace)||"hex")},[e]);let i=b.useMemo(()=>Qt(n,o,s).toLowerCase(),[n,o,s]),c=b.useCallback(f=>{let g=I(f),k=(g==null?void 0:g.value)||f||"";r(k),k===""&&(a(void 0),t(void 0)),g&&(a(g),l(g.colorSpace),t(g.value))},[t]),d=b.useCallback(()=>{let f=P.indexOf(s)+1;f>=P.length&&(f=0),l(P[f]);let g=(o==null?void 0:o[P[f]])||"";r(g),t(g)},[o,s,t]);return{value:n,realValue:i,updateValue:c,color:o,colorSpace:s,cycleColorSpace:d}},W=e=>e.replace(/\s*/,"").toLowerCase(),en=(e,t,n)=>{let[r,o]=b.useState(t!=null&&t.valid?[t]:[]);b.useEffect(()=>{t===void 0&&o([])},[t]);let a=b.useMemo(()=>(e||[]).map(l=>typeof l=="string"?I(l):l.title?{...I(l.color),keyword:l.title}:I(l.color)).concat(r).filter(Boolean).slice(-27),[e,r]),s=b.useCallback(l=>{l!=null&&l.valid&&(a.some(i=>W(i[n])===W(l[n]))||o(i=>i.concat(l)))},[n,a]);return{presets:a,addPreset:s}},tn=({name:e,value:t,onChange:n,onFocus:r,onBlur:o,presetColors:a,startOpen:s=!1})=>{let l=b.useCallback(Ht(n,200),[n]),{value:i,realValue:c,updateValue:d,color:f,colorSpace:g,cycleColorSpace:k}=Zt(t,l),{presets:N,addPreset:_}=en(a,f,g),p=Jt[g];return h.createElement(Pt,null,h.createElement(Lt,{startOpen:s,closeOnOutsideClick:!0,onVisibleChange:()=>_(f),tooltip:h.createElement(qt,null,h.createElement(p,{color:c==="transparent"?"#000000":c,onChange:d,onFocus:r,onBlur:o}),N.length>0&&h.createElement(Wt,null,N.map((m,x)=>h.createElement(ue,{key:`${m.value}-${x}`,hasChrome:!1,tooltip:h.createElement(Bt,{note:m.keyword||m.value})},h.createElement(ie,{value:m[g],active:f&&W(m[g])===W(f[g]),onClick:()=>d(m.value)})))))},h.createElement(ie,{value:c,style:{margin:4}})),h.createElement(Kt,{id:Ne(e),value:i,onChange:m=>d(m.target.value),onFocus:m=>m.target.select(),placeholder:"Choose color..."}),i?h.createElement(Vt,{onClick:k}):null)},un=tn;export{tn as ColorControl,un as default}; +import{n as M,e as ue,T as Me,F as Ce,f as $e,g as Ne}from"./index-iQvBBB1Z.js";import{R as h,r as b,g as fe}from"./index-BBkUAzwr.js";import{_ as Oe,i as J,a as Ie}from"./index-DrlA5mbP.js";import"./iframe-C3OM9s_A.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrFu-skq.js";function $(){return($=Object.assign||function(e){for(var t=1;t=0||(o[n]=e[n]);return o}function K(e){var t=b.useRef(e),n=b.useRef(function(r){t.current&&t.current(r)});return t.current=e,n.current}var S=function(e,t,n){return t===void 0&&(t=0),n===void 0&&(n=1),e>n?n:e0:p.buttons>0)&&o.current?a(ne(o.current,p,l.current)):_(!1)},N=function(){return _(!1)};function _(p){var m=i.current,x=V(o.current),C=p?x.addEventListener:x.removeEventListener;C(m?"touchmove":"mousemove",k),C(m?"touchend":"mouseup",N)}return[function(p){var m=p.nativeEvent,x=o.current;if(x&&(re(m),!function(X,R){return R&&!j(X)}(m,i.current)&&x)){if(j(m)){i.current=!0;var C=m.changedTouches||[];C.length&&(l.current=C[0].identifier)}x.focus(),a(ne(x,m,l.current)),_(!0)}},function(p){var m=p.which||p.keyCode;m<37||m>40||(p.preventDefault(),s({left:m===39?.05:m===37?-.05:0,top:m===40?.05:m===38?-.05:0}))},_]},[s,a]),d=c[0],f=c[1],g=c[2];return b.useEffect(function(){return g},[g]),h.createElement("div",$({},r,{onTouchStart:d,onMouseDown:d,className:"react-colorful__interactive",ref:o,onKeyDown:f,tabIndex:0,role:"slider"}))}),z=function(e){return e.filter(Boolean).join(" ")},ee=function(e){var t=e.color,n=e.left,r=e.top,o=r===void 0?.5:r,a=z(["react-colorful__pointer",e.className]);return h.createElement("div",{className:a,style:{top:100*o+"%",left:100*n+"%"}},h.createElement("div",{className:"react-colorful__pointer-fill",style:{backgroundColor:t}}))},y=function(e,t,n){return t===void 0&&(t=0),n===void 0&&(n=Math.pow(10,t)),Math.round(n*e)/n},Se={grad:.9,turn:360,rad:360/(2*Math.PI)},Re=function(e){return ge(A(e))},A=function(e){return e[0]==="#"&&(e=e.substring(1)),e.length<6?{r:parseInt(e[0]+e[0],16),g:parseInt(e[1]+e[1],16),b:parseInt(e[2]+e[2],16),a:e.length===4?y(parseInt(e[3]+e[3],16)/255,2):1}:{r:parseInt(e.substring(0,2),16),g:parseInt(e.substring(2,4),16),b:parseInt(e.substring(4,6),16),a:e.length===8?y(parseInt(e.substring(6,8),16)/255,2):1}},Te=function(e,t){return t===void 0&&(t="deg"),Number(e)*(Se[t]||1)},je=function(e){var t=/hsla?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i.exec(e);return t?Fe({h:Te(t[1],t[2]),s:Number(t[3]),l:Number(t[4]),a:t[5]===void 0?1:Number(t[5])/(t[6]?100:1)}):{h:0,s:0,v:0,a:1}},Fe=function(e){var t=e.s,n=e.l;return{h:e.h,s:(t*=(n<50?n:100-n)/100)>0?2*t/(n+t)*100:0,v:n+t,a:e.a}},ze=function(e){return Pe(de(e))},he=function(e){var t=e.s,n=e.v,r=e.a,o=(200-t)*n/100;return{h:y(e.h),s:y(o>0&&o<200?t*n/100/(o<=100?o:200-o)*100:0),l:y(o/2),a:y(r,2)}},G=function(e){var t=he(e);return"hsl("+t.h+", "+t.s+"%, "+t.l+"%)"},q=function(e){var t=he(e);return"hsla("+t.h+", "+t.s+"%, "+t.l+"%, "+t.a+")"},de=function(e){var t=e.h,n=e.s,r=e.v,o=e.a;t=t/360*6,n/=100,r/=100;var a=Math.floor(t),s=r*(1-n),l=r*(1-(t-a)*n),i=r*(1-(1-t+a)*n),c=a%6;return{r:y(255*[r,l,s,s,i,r][c]),g:y(255*[i,r,r,l,s,s][c]),b:y(255*[s,s,i,r,r,l][c]),a:y(o,2)}},He=function(e){var t=/rgba?\(?\s*(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i.exec(e);return t?ge({r:Number(t[1])/(t[2]?100/255:1),g:Number(t[3])/(t[4]?100/255:1),b:Number(t[5])/(t[6]?100/255:1),a:t[7]===void 0?1:Number(t[7])/(t[8]?100:1)}):{h:0,s:0,v:0,a:1}},H=function(e){var t=e.toString(16);return t.length<2?"0"+t:t},Pe=function(e){var t=e.r,n=e.g,r=e.b,o=e.a,a=o<1?H(y(255*o)):"";return"#"+H(t)+H(n)+H(r)+a},ge=function(e){var t=e.r,n=e.g,r=e.b,o=e.a,a=Math.max(t,n,r),s=a-Math.min(t,n,r),l=s?a===t?(n-r)/s:a===n?2+(r-t)/s:4+(t-n)/s:0;return{h:y(60*(l<0?l+6:l)),s:y(a?s/a*100:0),v:y(a/255*100),a:o}},me=h.memo(function(e){var t=e.hue,n=e.onChange,r=z(["react-colorful__hue",e.className]);return h.createElement("div",{className:r},h.createElement(Z,{onMove:function(o){n({h:360*o.left})},onKey:function(o){n({h:S(t+360*o.left,0,360)})},"aria-label":"Hue","aria-valuenow":y(t),"aria-valuemax":"360","aria-valuemin":"0"},h.createElement(ee,{className:"react-colorful__hue-pointer",left:t/360,color:G({h:t,s:100,v:100,a:1})})))}),be=h.memo(function(e){var t=e.hsva,n=e.onChange,r={backgroundColor:G({h:t.h,s:100,v:100,a:1})};return h.createElement("div",{className:"react-colorful__saturation",style:r},h.createElement(Z,{onMove:function(o){n({s:100*o.left,v:100-100*o.top})},onKey:function(o){n({s:S(t.s+100*o.left,0,100),v:S(t.v-100*o.top,0,100)})},"aria-label":"Color","aria-valuetext":"Saturation "+y(t.s)+"%, Brightness "+y(t.v)+"%"},h.createElement(ee,{className:"react-colorful__saturation-pointer",top:1-t.v/100,left:t.s/100,color:G(t)})))}),ve=function(e,t){if(e===t)return!0;for(var n in e)if(e[n]!==t[n])return!1;return!0},pe=function(e,t){return e.replace(/\s/g,"")===t.replace(/\s/g,"")},Le=function(e,t){return e.toLowerCase()===t.toLowerCase()||ve(A(e),A(t))};function ye(e,t,n){var r=K(n),o=b.useState(function(){return e.toHsva(t)}),a=o[0],s=o[1],l=b.useRef({color:t,hsva:a});b.useEffect(function(){if(!e.equal(t,l.current.color)){var c=e.toHsva(t);l.current={hsva:c,color:t},s(c)}},[t,e]),b.useEffect(function(){var c;ve(a,l.current.hsva)||e.equal(c=e.fromHsva(a),l.current.color)||(l.current={hsva:a,color:c},r(c))},[a,e,r]);var i=b.useCallback(function(c){s(function(d){return Object.assign({},d,c)})},[]);return[a,i]}var qe=typeof window<"u"?b.useLayoutEffect:b.useEffect,Be=function(){return typeof __webpack_nonce__<"u"?__webpack_nonce__:void 0},oe=new Map,xe=function(e){qe(function(){var t=e.current?e.current.ownerDocument:document;if(t!==void 0&&!oe.has(t)){var n=t.createElement("style");n.innerHTML=`.react-colorful{position:relative;display:flex;flex-direction:column;width:200px;height:200px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.react-colorful__saturation{position:relative;flex-grow:1;border-color:transparent;border-bottom:12px solid #000;border-radius:8px 8px 0 0;background-image:linear-gradient(0deg,#000,transparent),linear-gradient(90deg,#fff,hsla(0,0%,100%,0))}.react-colorful__alpha-gradient,.react-colorful__pointer-fill{content:"";position:absolute;left:0;top:0;right:0;bottom:0;pointer-events:none;border-radius:inherit}.react-colorful__alpha-gradient,.react-colorful__saturation{box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}.react-colorful__alpha,.react-colorful__hue{position:relative;height:24px}.react-colorful__hue{background:linear-gradient(90deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red)}.react-colorful__last-control{border-radius:0 0 8px 8px}.react-colorful__interactive{position:absolute;left:0;top:0;right:0;bottom:0;border-radius:inherit;outline:none;touch-action:none}.react-colorful__pointer{position:absolute;z-index:1;box-sizing:border-box;width:28px;height:28px;transform:translate(-50%,-50%);background-color:#fff;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2)}.react-colorful__interactive:focus .react-colorful__pointer{transform:translate(-50%,-50%) scale(1.1)}.react-colorful__alpha,.react-colorful__alpha-pointer{background-color:#fff;background-image:url('data:image/svg+xml;charset=utf-8,')}.react-colorful__saturation-pointer{z-index:3}.react-colorful__hue-pointer{z-index:2}`,oe.set(t,n);var r=Be();r&&n.setAttribute("nonce",r),t.head.appendChild(n)}},[])},We=function(e){var t=e.className,n=e.colorModel,r=e.color,o=r===void 0?n.defaultColor:r,a=e.onChange,s=Q(e,["className","colorModel","color","onChange"]),l=b.useRef(null);xe(l);var i=ye(n,o,a),c=i[0],d=i[1],f=z(["react-colorful",t]);return h.createElement("div",$({},s,{ref:l,className:f}),h.createElement(be,{hsva:c,onChange:d}),h.createElement(me,{hue:c.h,onChange:d,className:"react-colorful__last-control"}))},Xe={defaultColor:"000",toHsva:Re,fromHsva:function(e){return ze({h:e.h,s:e.s,v:e.v,a:1})},equal:Le},De=function(e){return h.createElement(We,$({},e,{colorModel:Xe}))},Ke=function(e){var t=e.className,n=e.hsva,r=e.onChange,o={backgroundImage:"linear-gradient(90deg, "+q(Object.assign({},n,{a:0}))+", "+q(Object.assign({},n,{a:1}))+")"},a=z(["react-colorful__alpha",t]),s=y(100*n.a);return h.createElement("div",{className:a},h.createElement("div",{className:"react-colorful__alpha-gradient",style:o}),h.createElement(Z,{onMove:function(l){r({a:l.left})},onKey:function(l){r({a:S(n.a+l.left)})},"aria-label":"Alpha","aria-valuetext":s+"%","aria-valuenow":s,"aria-valuemin":"0","aria-valuemax":"100"},h.createElement(ee,{className:"react-colorful__alpha-pointer",left:n.a,color:q(n)})))},we=function(e){var t=e.className,n=e.colorModel,r=e.color,o=r===void 0?n.defaultColor:r,a=e.onChange,s=Q(e,["className","colorModel","color","onChange"]),l=b.useRef(null);xe(l);var i=ye(n,o,a),c=i[0],d=i[1],f=z(["react-colorful",t]);return h.createElement("div",$({},s,{ref:l,className:f}),h.createElement(be,{hsva:c,onChange:d}),h.createElement(me,{hue:c.h,onChange:d}),h.createElement(Ke,{hsva:c,onChange:d,className:"react-colorful__last-control"}))},Ve={defaultColor:"hsla(0, 0%, 0%, 1)",toHsva:je,fromHsva:q,equal:pe},Ae=function(e){return h.createElement(we,$({},e,{colorModel:Ve}))},Ge={defaultColor:"rgba(0, 0, 0, 1)",toHsva:He,fromHsva:function(e){var t=de(e);return"rgba("+t.r+", "+t.g+", "+t.b+", "+t.a+")"},equal:pe},Ue=function(e){return h.createElement(we,$({},e,{colorModel:Ge}))},Ye={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]};const F=Ye,ke={};for(const e of Object.keys(F))ke[F[e]]=e;const u={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};var _e=u;for(const e of Object.keys(u)){if(!("channels"in u[e]))throw new Error("missing channels property: "+e);if(!("labels"in u[e]))throw new Error("missing channel labels property: "+e);if(u[e].labels.length!==u[e].channels)throw new Error("channel and label counts mismatch: "+e);const{channels:t,labels:n}=u[e];delete u[e].channels,delete u[e].labels,Object.defineProperty(u[e],"channels",{value:t}),Object.defineProperty(u[e],"labels",{value:n})}u.rgb.hsl=function(e){const t=e[0]/255,n=e[1]/255,r=e[2]/255,o=Math.min(t,n,r),a=Math.max(t,n,r),s=a-o;let l,i;a===o?l=0:t===a?l=(n-r)/s:n===a?l=2+(r-t)/s:r===a&&(l=4+(t-n)/s),l=Math.min(l*60,360),l<0&&(l+=360);const c=(o+a)/2;return a===o?i=0:c<=.5?i=s/(a+o):i=s/(2-a-o),[l,i*100,c*100]};u.rgb.hsv=function(e){let t,n,r,o,a;const s=e[0]/255,l=e[1]/255,i=e[2]/255,c=Math.max(s,l,i),d=c-Math.min(s,l,i),f=function(g){return(c-g)/6/d+1/2};return d===0?(o=0,a=0):(a=d/c,t=f(s),n=f(l),r=f(i),s===c?o=r-n:l===c?o=1/3+t-r:i===c&&(o=2/3+n-t),o<0?o+=1:o>1&&(o-=1)),[o*360,a*100,c*100]};u.rgb.hwb=function(e){const t=e[0],n=e[1];let r=e[2];const o=u.rgb.hsl(e)[0],a=1/255*Math.min(t,Math.min(n,r));return r=1-1/255*Math.max(t,Math.max(n,r)),[o,a*100,r*100]};u.rgb.cmyk=function(e){const t=e[0]/255,n=e[1]/255,r=e[2]/255,o=Math.min(1-t,1-n,1-r),a=(1-t-o)/(1-o)||0,s=(1-n-o)/(1-o)||0,l=(1-r-o)/(1-o)||0;return[a*100,s*100,l*100,o*100]};function Je(e,t){return(e[0]-t[0])**2+(e[1]-t[1])**2+(e[2]-t[2])**2}u.rgb.keyword=function(e){const t=ke[e];if(t)return t;let n=1/0,r;for(const o of Object.keys(F)){const a=F[o],s=Je(e,a);s.04045?((t+.055)/1.055)**2.4:t/12.92,n=n>.04045?((n+.055)/1.055)**2.4:n/12.92,r=r>.04045?((r+.055)/1.055)**2.4:r/12.92;const o=t*.4124+n*.3576+r*.1805,a=t*.2126+n*.7152+r*.0722,s=t*.0193+n*.1192+r*.9505;return[o*100,a*100,s*100]};u.rgb.lab=function(e){const t=u.rgb.xyz(e);let n=t[0],r=t[1],o=t[2];n/=95.047,r/=100,o/=108.883,n=n>.008856?n**(1/3):7.787*n+16/116,r=r>.008856?r**(1/3):7.787*r+16/116,o=o>.008856?o**(1/3):7.787*o+16/116;const a=116*r-16,s=500*(n-r),l=200*(r-o);return[a,s,l]};u.hsl.rgb=function(e){const t=e[0]/360,n=e[1]/100,r=e[2]/100;let o,a,s;if(n===0)return s=r*255,[s,s,s];r<.5?o=r*(1+n):o=r+n-r*n;const l=2*r-o,i=[0,0,0];for(let c=0;c<3;c++)a=t+1/3*-(c-1),a<0&&a++,a>1&&a--,6*a<1?s=l+(o-l)*6*a:2*a<1?s=o:3*a<2?s=l+(o-l)*(2/3-a)*6:s=l,i[c]=s*255;return i};u.hsl.hsv=function(e){const t=e[0];let n=e[1]/100,r=e[2]/100,o=n;const a=Math.max(r,.01);r*=2,n*=r<=1?r:2-r,o*=a<=1?a:2-a;const s=(r+n)/2,l=r===0?2*o/(a+o):2*n/(r+n);return[t,l*100,s*100]};u.hsv.rgb=function(e){const t=e[0]/60,n=e[1]/100;let r=e[2]/100;const o=Math.floor(t)%6,a=t-Math.floor(t),s=255*r*(1-n),l=255*r*(1-n*a),i=255*r*(1-n*(1-a));switch(r*=255,o){case 0:return[r,i,s];case 1:return[l,r,s];case 2:return[s,r,i];case 3:return[s,l,r];case 4:return[i,s,r];case 5:return[r,s,l]}};u.hsv.hsl=function(e){const t=e[0],n=e[1]/100,r=e[2]/100,o=Math.max(r,.01);let a,s;s=(2-n)*r;const l=(2-n)*o;return a=n*o,a/=l<=1?l:2-l,a=a||0,s/=2,[t,a*100,s*100]};u.hwb.rgb=function(e){const t=e[0]/360;let n=e[1]/100,r=e[2]/100;const o=n+r;let a;o>1&&(n/=o,r/=o);const s=Math.floor(6*t),l=1-r;a=6*t-s,s&1&&(a=1-a);const i=n+a*(l-n);let c,d,f;switch(s){default:case 6:case 0:c=l,d=i,f=n;break;case 1:c=i,d=l,f=n;break;case 2:c=n,d=l,f=i;break;case 3:c=n,d=i,f=l;break;case 4:c=i,d=n,f=l;break;case 5:c=l,d=n,f=i;break}return[c*255,d*255,f*255]};u.cmyk.rgb=function(e){const t=e[0]/100,n=e[1]/100,r=e[2]/100,o=e[3]/100,a=1-Math.min(1,t*(1-o)+o),s=1-Math.min(1,n*(1-o)+o),l=1-Math.min(1,r*(1-o)+o);return[a*255,s*255,l*255]};u.xyz.rgb=function(e){const t=e[0]/100,n=e[1]/100,r=e[2]/100;let o,a,s;return o=t*3.2406+n*-1.5372+r*-.4986,a=t*-.9689+n*1.8758+r*.0415,s=t*.0557+n*-.204+r*1.057,o=o>.0031308?1.055*o**(1/2.4)-.055:o*12.92,a=a>.0031308?1.055*a**(1/2.4)-.055:a*12.92,s=s>.0031308?1.055*s**(1/2.4)-.055:s*12.92,o=Math.min(Math.max(0,o),1),a=Math.min(Math.max(0,a),1),s=Math.min(Math.max(0,s),1),[o*255,a*255,s*255]};u.xyz.lab=function(e){let t=e[0],n=e[1],r=e[2];t/=95.047,n/=100,r/=108.883,t=t>.008856?t**(1/3):7.787*t+16/116,n=n>.008856?n**(1/3):7.787*n+16/116,r=r>.008856?r**(1/3):7.787*r+16/116;const o=116*n-16,a=500*(t-n),s=200*(n-r);return[o,a,s]};u.lab.xyz=function(e){const t=e[0],n=e[1],r=e[2];let o,a,s;a=(t+16)/116,o=n/500+a,s=a-r/200;const l=a**3,i=o**3,c=s**3;return a=l>.008856?l:(a-16/116)/7.787,o=i>.008856?i:(o-16/116)/7.787,s=c>.008856?c:(s-16/116)/7.787,o*=95.047,a*=100,s*=108.883,[o,a,s]};u.lab.lch=function(e){const t=e[0],n=e[1],r=e[2];let o;o=Math.atan2(r,n)*360/2/Math.PI,o<0&&(o+=360);const s=Math.sqrt(n*n+r*r);return[t,s,o]};u.lch.lab=function(e){const t=e[0],n=e[1],o=e[2]/360*2*Math.PI,a=n*Math.cos(o),s=n*Math.sin(o);return[t,a,s]};u.rgb.ansi16=function(e,t=null){const[n,r,o]=e;let a=t===null?u.rgb.hsv(e)[2]:t;if(a=Math.round(a/50),a===0)return 30;let s=30+(Math.round(o/255)<<2|Math.round(r/255)<<1|Math.round(n/255));return a===2&&(s+=60),s};u.hsv.ansi16=function(e){return u.rgb.ansi16(u.hsv.rgb(e),e[2])};u.rgb.ansi256=function(e){const t=e[0],n=e[1],r=e[2];return t===n&&n===r?t<8?16:t>248?231:Math.round((t-8)/247*24)+232:16+36*Math.round(t/255*5)+6*Math.round(n/255*5)+Math.round(r/255*5)};u.ansi16.rgb=function(e){let t=e%10;if(t===0||t===7)return e>50&&(t+=3.5),t=t/10.5*255,[t,t,t];const n=(~~(e>50)+1)*.5,r=(t&1)*n*255,o=(t>>1&1)*n*255,a=(t>>2&1)*n*255;return[r,o,a]};u.ansi256.rgb=function(e){if(e>=232){const a=(e-232)*10+8;return[a,a,a]}e-=16;let t;const n=Math.floor(e/36)/5*255,r=Math.floor((t=e%36)/6)/5*255,o=t%6/5*255;return[n,r,o]};u.rgb.hex=function(e){const n=(((Math.round(e[0])&255)<<16)+((Math.round(e[1])&255)<<8)+(Math.round(e[2])&255)).toString(16).toUpperCase();return"000000".substring(n.length)+n};u.hex.rgb=function(e){const t=e.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!t)return[0,0,0];let n=t[0];t[0].length===3&&(n=n.split("").map(l=>l+l).join(""));const r=parseInt(n,16),o=r>>16&255,a=r>>8&255,s=r&255;return[o,a,s]};u.rgb.hcg=function(e){const t=e[0]/255,n=e[1]/255,r=e[2]/255,o=Math.max(Math.max(t,n),r),a=Math.min(Math.min(t,n),r),s=o-a;let l,i;return s<1?l=a/(1-s):l=0,s<=0?i=0:o===t?i=(n-r)/s%6:o===n?i=2+(r-t)/s:i=4+(t-n)/s,i/=6,i%=1,[i*360,s*100,l*100]};u.hsl.hcg=function(e){const t=e[1]/100,n=e[2]/100,r=n<.5?2*t*n:2*t*(1-n);let o=0;return r<1&&(o=(n-.5*r)/(1-r)),[e[0],r*100,o*100]};u.hsv.hcg=function(e){const t=e[1]/100,n=e[2]/100,r=t*n;let o=0;return r<1&&(o=(n-r)/(1-r)),[e[0],r*100,o*100]};u.hcg.rgb=function(e){const t=e[0]/360,n=e[1]/100,r=e[2]/100;if(n===0)return[r*255,r*255,r*255];const o=[0,0,0],a=t%1*6,s=a%1,l=1-s;let i=0;switch(Math.floor(a)){case 0:o[0]=1,o[1]=s,o[2]=0;break;case 1:o[0]=l,o[1]=1,o[2]=0;break;case 2:o[0]=0,o[1]=1,o[2]=s;break;case 3:o[0]=0,o[1]=l,o[2]=1;break;case 4:o[0]=s,o[1]=0,o[2]=1;break;default:o[0]=1,o[1]=0,o[2]=l}return i=(1-n)*r,[(n*o[0]+i)*255,(n*o[1]+i)*255,(n*o[2]+i)*255]};u.hcg.hsv=function(e){const t=e[1]/100,n=e[2]/100,r=t+n*(1-t);let o=0;return r>0&&(o=t/r),[e[0],o*100,r*100]};u.hcg.hsl=function(e){const t=e[1]/100,r=e[2]/100*(1-t)+.5*t;let o=0;return r>0&&r<.5?o=t/(2*r):r>=.5&&r<1&&(o=t/(2*(1-r))),[e[0],o*100,r*100]};u.hcg.hwb=function(e){const t=e[1]/100,n=e[2]/100,r=t+n*(1-t);return[e[0],(r-t)*100,(1-r)*100]};u.hwb.hcg=function(e){const t=e[1]/100,r=1-e[2]/100,o=r-t;let a=0;return o<1&&(a=(r-o)/(1-o)),[e[0],o*100,a*100]};u.apple.rgb=function(e){return[e[0]/65535*255,e[1]/65535*255,e[2]/65535*255]};u.rgb.apple=function(e){return[e[0]/255*65535,e[1]/255*65535,e[2]/255*65535]};u.gray.rgb=function(e){return[e[0]/100*255,e[0]/100*255,e[0]/100*255]};u.gray.hsl=function(e){return[0,0,e[0]]};u.gray.hsv=u.gray.hsl;u.gray.hwb=function(e){return[0,100,e[0]]};u.gray.cmyk=function(e){return[0,0,0,e[0]]};u.gray.lab=function(e){return[e[0],0,0]};u.gray.hex=function(e){const t=Math.round(e[0]/100*255)&255,r=((t<<16)+(t<<8)+t).toString(16).toUpperCase();return"000000".substring(r.length)+r};u.rgb.gray=function(e){return[(e[0]+e[1]+e[2])/3/255*100]};const B=_e;function Qe(){const e={},t=Object.keys(B);for(let n=t.length,r=0;r1&&(n=r),e(n))};return"conversion"in e&&(t.conversion=e.conversion),t}function st(e){const t=function(...n){const r=n[0];if(r==null)return r;r.length>1&&(n=r);const o=e(n);if(typeof o=="object")for(let a=o.length,s=0;s{O[e]={},Object.defineProperty(O[e],"channels",{value:U[e].channels}),Object.defineProperty(O[e],"labels",{value:U[e].labels});const t=rt(e);Object.keys(t).forEach(r=>{const o=t[r];O[e][r]=st(o),O[e][r].raw=at(o)})});var lt=O;const w=fe(lt);var it=Oe,ct=function(){return it.Date.now()},ut=ct,ft=/\s/;function ht(e){for(var t=e.length;t--&&ft.test(e.charAt(t)););return t}var dt=ht,gt=dt,mt=/^\s+/;function bt(e){return e&&e.slice(0,gt(e)+1).replace(mt,"")}var vt=bt,pt=vt,ae=J,yt=Ie,se=NaN,xt=/^[-+]0x[0-9a-f]+$/i,wt=/^0b[01]+$/i,kt=/^0o[0-7]+$/i,_t=parseInt;function Et(e){if(typeof e=="number")return e;if(yt(e))return se;if(ae(e)){var t=typeof e.valueOf=="function"?e.valueOf():e;e=ae(t)?t+"":t}if(typeof e!="string")return e===0?e:+e;e=pt(e);var n=wt.test(e);return n||kt.test(e)?_t(e.slice(2),n?2:8):xt.test(e)?se:+e}var Mt=Et,Ct=J,D=ut,le=Mt,$t="Expected a function",Nt=Math.max,Ot=Math.min;function It(e,t,n){var r,o,a,s,l,i,c=0,d=!1,f=!1,g=!0;if(typeof e!="function")throw new TypeError($t);t=le(t)||0,Ct(n)&&(d=!!n.leading,f="maxWait"in n,a=f?Nt(le(n.maxWait)||0,t):a,g="trailing"in n?!!n.trailing:g);function k(v){var E=r,T=o;return r=o=void 0,c=v,s=e.apply(T,E),s}function N(v){return c=v,l=setTimeout(m,t),d?k(v):s}function _(v){var E=v-i,T=v-c,te=t-E;return f?Ot(te,a-T):te}function p(v){var E=v-i,T=v-c;return i===void 0||E>=t||E<0||f&&T>=a}function m(){var v=D();if(p(v))return x(v);l=setTimeout(m,_(v))}function x(v){return l=void 0,g&&r?k(v):(r=o=void 0,s)}function C(){l!==void 0&&clearTimeout(l),c=0,r=i=o=l=void 0}function X(){return l===void 0?s:x(D())}function R(){var v=D(),E=p(v);if(r=arguments,o=this,i=v,E){if(l===void 0)return N(i);if(f)return clearTimeout(l),l=setTimeout(m,t),k(i)}return l===void 0&&(l=setTimeout(m,t)),s}return R.cancel=C,R.flush=X,R}var St=It,Rt=St,Tt=J,jt="Expected a function";function Ft(e,t,n){var r=!0,o=!0;if(typeof e!="function")throw new TypeError(jt);return Tt(n)&&(r="leading"in n?!!n.leading:r,o="trailing"in n?!!n.trailing:o),Rt(e,t,{leading:r,maxWait:t,trailing:o})}var zt=Ft;const Ht=fe(zt);var Pt=M.div({position:"relative",maxWidth:250}),Lt=M(ue)({position:"absolute",zIndex:1,top:4,left:4}),qt=M.div({width:200,margin:5,".react-colorful__saturation":{borderRadius:"4px 4px 0 0"},".react-colorful__hue":{boxShadow:"inset 0 0 0 1px rgb(0 0 0 / 5%)"},".react-colorful__last-control":{borderRadius:"0 0 4px 4px"}}),Bt=M(Me)(({theme:e})=>({fontFamily:e.typography.fonts.base})),Wt=M.div({display:"grid",gridTemplateColumns:"repeat(9, 16px)",gap:6,padding:3,marginTop:5,width:200}),Xt=M.div(({theme:e,active:t})=>({width:16,height:16,boxShadow:t?`${e.appBorderColor} 0 0 0 1px inset, ${e.textMutedColor}50 0 0 0 4px`:`${e.appBorderColor} 0 0 0 1px inset`,borderRadius:e.appBorderRadius})),Dt=`url('data:image/svg+xml;charset=utf-8,')`,ie=({value:e,active:t,onClick:n,style:r,...o})=>{let a=`linear-gradient(${e}, ${e}), ${Dt}, linear-gradient(#fff, #fff)`;return h.createElement(Xt,{...o,active:t,onClick:n,style:{...r,backgroundImage:a}})},Kt=M(Ce.Input)(({theme:e})=>({width:"100%",paddingLeft:30,paddingRight:30,boxSizing:"border-box",fontFamily:e.typography.fonts.base})),Vt=M($e)(({theme:e})=>({position:"absolute",zIndex:1,top:6,right:7,width:20,height:20,padding:4,boxSizing:"border-box",cursor:"pointer",color:e.input.color})),Ee=(e=>(e.RGB="rgb",e.HSL="hsl",e.HEX="hex",e))(Ee||{}),P=Object.values(Ee),At=/\(([0-9]+),\s*([0-9]+)%?,\s*([0-9]+)%?,?\s*([0-9.]+)?\)/,Gt=/^\s*rgba?\(([0-9]+),\s*([0-9]+),\s*([0-9]+),?\s*([0-9.]+)?\)\s*$/i,Ut=/^\s*hsla?\(([0-9]+),\s*([0-9]+)%,\s*([0-9]+)%,?\s*([0-9.]+)?\)\s*$/i,Y=/^\s*#?([0-9a-f]{3}|[0-9a-f]{6})\s*$/i,Yt=/^\s*#?([0-9a-f]{3})\s*$/i,Jt={hex:De,rgb:Ue,hsl:Ae},L={hex:"transparent",rgb:"rgba(0, 0, 0, 0)",hsl:"hsla(0, 0%, 0%, 0)"},ce=e=>{let t=e==null?void 0:e.match(At);if(!t)return[0,0,0,1];let[,n,r,o,a=1]=t;return[n,r,o,a].map(Number)},I=e=>{if(!e)return;let t=!0;if(Gt.test(e)){let[s,l,i,c]=ce(e),[d,f,g]=w.rgb.hsl([s,l,i])||[0,0,0];return{valid:t,value:e,keyword:w.rgb.keyword([s,l,i]),colorSpace:"rgb",rgb:e,hsl:`hsla(${d}, ${f}%, ${g}%, ${c})`,hex:`#${w.rgb.hex([s,l,i]).toLowerCase()}`}}if(Ut.test(e)){let[s,l,i,c]=ce(e),[d,f,g]=w.hsl.rgb([s,l,i])||[0,0,0];return{valid:t,value:e,keyword:w.hsl.keyword([s,l,i]),colorSpace:"hsl",rgb:`rgba(${d}, ${f}, ${g}, ${c})`,hsl:e,hex:`#${w.hsl.hex([s,l,i]).toLowerCase()}`}}let n=e.replace("#",""),r=w.keyword.rgb(n)||w.hex.rgb(n),o=w.rgb.hsl(r),a=e;if(/[^#a-f0-9]/i.test(e)?a=n:Y.test(e)&&(a=`#${n}`),a.startsWith("#"))t=Y.test(a);else try{w.keyword.hex(a)}catch{t=!1}return{valid:t,value:a,keyword:w.rgb.keyword(r),colorSpace:"hex",rgb:`rgba(${r[0]}, ${r[1]}, ${r[2]}, 1)`,hsl:`hsla(${o[0]}, ${o[1]}%, ${o[2]}%, 1)`,hex:a}},Qt=(e,t,n)=>{if(!e||!(t!=null&&t.valid))return L[n];if(n!=="hex")return(t==null?void 0:t[n])||L[n];if(!t.hex.startsWith("#"))try{return`#${w.keyword.hex(t.hex)}`}catch{return L.hex}let r=t.hex.match(Yt);if(!r)return Y.test(t.hex)?t.hex:L.hex;let[o,a,s]=r[1].split("");return`#${o}${o}${a}${a}${s}${s}`},Zt=(e,t)=>{let[n,r]=b.useState(e||""),[o,a]=b.useState(()=>I(n)),[s,l]=b.useState((o==null?void 0:o.colorSpace)||"hex");b.useEffect(()=>{let f=e||"",g=I(f);r(f),a(g),l((g==null?void 0:g.colorSpace)||"hex")},[e]);let i=b.useMemo(()=>Qt(n,o,s).toLowerCase(),[n,o,s]),c=b.useCallback(f=>{let g=I(f),k=(g==null?void 0:g.value)||f||"";r(k),k===""&&(a(void 0),t(void 0)),g&&(a(g),l(g.colorSpace),t(g.value))},[t]),d=b.useCallback(()=>{let f=P.indexOf(s)+1;f>=P.length&&(f=0),l(P[f]);let g=(o==null?void 0:o[P[f]])||"";r(g),t(g)},[o,s,t]);return{value:n,realValue:i,updateValue:c,color:o,colorSpace:s,cycleColorSpace:d}},W=e=>e.replace(/\s*/,"").toLowerCase(),en=(e,t,n)=>{let[r,o]=b.useState(t!=null&&t.valid?[t]:[]);b.useEffect(()=>{t===void 0&&o([])},[t]);let a=b.useMemo(()=>(e||[]).map(l=>typeof l=="string"?I(l):l.title?{...I(l.color),keyword:l.title}:I(l.color)).concat(r).filter(Boolean).slice(-27),[e,r]),s=b.useCallback(l=>{l!=null&&l.valid&&(a.some(i=>W(i[n])===W(l[n]))||o(i=>i.concat(l)))},[n,a]);return{presets:a,addPreset:s}},tn=({name:e,value:t,onChange:n,onFocus:r,onBlur:o,presetColors:a,startOpen:s=!1})=>{let l=b.useCallback(Ht(n,200),[n]),{value:i,realValue:c,updateValue:d,color:f,colorSpace:g,cycleColorSpace:k}=Zt(t,l),{presets:N,addPreset:_}=en(a,f,g),p=Jt[g];return h.createElement(Pt,null,h.createElement(Lt,{startOpen:s,closeOnOutsideClick:!0,onVisibleChange:()=>_(f),tooltip:h.createElement(qt,null,h.createElement(p,{color:c==="transparent"?"#000000":c,onChange:d,onFocus:r,onBlur:o}),N.length>0&&h.createElement(Wt,null,N.map((m,x)=>h.createElement(ue,{key:`${m.value}-${x}`,hasChrome:!1,tooltip:h.createElement(Bt,{note:m.keyword||m.value})},h.createElement(ie,{value:m[g],active:f&&W(m[g])===W(f[g]),onClick:()=>d(m.value)})))))},h.createElement(ie,{value:c,style:{margin:4}})),h.createElement(Kt,{id:Ne(e),value:i,onChange:m=>d(m.target.value),onFocus:m=>m.target.select(),placeholder:"Choose color..."}),i?h.createElement(Vt,{onClick:k}):null)},un=tn;export{tn as ColorControl,un as default}; diff --git a/docs/assets/Contribute-DyWf3n8a.js b/docs/assets/Contribute-DyWf3n8a.js new file mode 100644 index 0000000..a236786 --- /dev/null +++ b/docs/assets/Contribute-DyWf3n8a.js @@ -0,0 +1,95 @@ +import{j as e}from"./jsx-runtime-DRTy3Uxn.js";import{useMDXComponents as i}from"./index-z5U8iC57.js";import{M as o}from"./index-iQvBBB1Z.js";import"./index-BBkUAzwr.js";import"./iframe-C3OM9s_A.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";function s(r){const n={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...i(),...r.components};return e.jsxs(e.Fragment,{children:[e.jsx(o,{title:"Contribute"}),` +`,e.jsx(n.h1,{id:"contribute",children:"Contribute"}),` +`,e.jsxs(n.p,{children:[e.jsx(n.strong,{children:"nbody"})," is maintained by the open-source ",e.jsx(n.a,{href:"https://github.com/source-academy/",rel:"nofollow",children:"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."]}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsxs(n.li,{children:[e.jsx(n.a,{href:"#developer-guide",children:"Developer Guide"}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsx(n.li,{children:e.jsx(n.a,{href:"#clone-the-repository",children:"Clone the repository"})}),` +`,e.jsx(n.li,{children:e.jsx(n.a,{href:"#install-packages",children:"Install packages"})}),` +`,e.jsx(n.li,{children:e.jsx(n.a,{href:"#developer-setup",children:"Developer Setup"})}),` +`,e.jsx(n.li,{children:e.jsx(n.a,{href:"#directory-structure",children:"Directory Structure"})}),` +`,e.jsx(n.li,{children:e.jsx(n.a,{href:"#cli-commands",children:"CLI Commands"})}),` +`]}),` +`]}),` +`,e.jsx(n.li,{children:e.jsx(n.a,{href:"#roadmap",children:"Roadmap"})}),` +`]}),` +`,e.jsx(n.h2,{id:"developer-guide",children:"Developer Guide"}),` +`,e.jsxs(n.p,{children:["Here's a quick guide to get started on contributing to ",e.jsx(n.em,{children:"nbody"}),"."]}),` +`,e.jsx(n.h3,{id:"clone-the-repository",children:"Clone the repository"}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-bash",children:`git clone https://github.com/source-academy/nbody +`})}),` +`,e.jsx(n.h3,{id:"install-packages",children:"Install packages"}),` +`,e.jsxs(n.p,{children:["We use ",e.jsx(n.a,{href:"https://classic.yarnpkg.com/lang/en/",rel:"nofollow",children:"Yarn 1"})," as the package manager for its speed and developer experience. This installs all depenedencies required for the development of the project."]}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-bash",children:`cd nbody\r +yarn install +`})}),` +`,e.jsx(n.h3,{id:"developer-setup",children:"Developer Setup"}),` +`,e.jsx(n.p,{children:"Core libraries used in the project"}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsxs(n.li,{children:[e.jsx(n.a,{href:"https://threejs.org/",rel:"nofollow",children:"three"})," - For 3D rendering"]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.a,{href:"https://plotly.com/javascript/",rel:"nofollow",children:"plotly.js"})," - For 2D rendering"]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.a,{href:"https://lil-gui.georgealways.com/",rel:"nofollow",children:"lil-gui"})," - For GUI controls"]}),` +`]}),` +`,e.jsx(n.p,{children:"Development setup of the project"}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsxs(n.li,{children:["Source code is written in ES6 format in ",e.jsx(n.a,{href:"https://www.typescriptlang.org/",rel:"nofollow",children:"Typescript"})]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.a,{href:"https://eslint.org/",rel:"nofollow",children:"ESLint"})," for linting and ensuring code quality. If developing in VSCode, install the ESLint extension for real-time linting and format on save."]}),` +`,e.jsxs(n.li,{children:["Code comments in JSdoc format, API documentation generated using ",e.jsx(n.a,{href:"https://typedoc.org/",rel:"nofollow",children:"Typedoc"})]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.a,{href:"https://storybook.js.org/",rel:"nofollow",children:"Storybook"})," for general documentation and demo showcase",` +`,e.jsxs(n.ul,{children:[` +`,e.jsxs(n.li,{children:[e.jsx(n.a,{href:"https://react.dev/",rel:"nofollow",children:"React"})," and ",e.jsx(n.a,{href:"https://vitejs.dev/",rel:"nofollow",children:"Vite"})," template for component development"]}),` +`]}),` +`]}),` +`]}),` +`,e.jsx(n.h3,{id:"directory-structure",children:"Directory Structure"}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsxs(n.li,{children:[e.jsx(n.code,{children:".storybook"}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsx(n.li,{children:"Contains configuration and components for Storybook documentation"}),` +`]}),` +`]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.code,{children:"dist"}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsx(n.li,{children:"Contains transpiled code along with type definitions"}),` +`]}),` +`]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.code,{children:"docs"}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsx(n.li,{children:"Built documentation"}),` +`,e.jsxs(n.li,{children:[e.jsx(n.code,{children:"api"}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsx(n.li,{children:"Built API documentation"}),` +`]}),` +`]}),` +`]}),` +`]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.code,{children:"scripts"}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsx(n.li,{children:"Contains miscellaneous scripts for development like clean"}),` +`]}),` +`]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.code,{children:"src"}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsx(n.li,{children:"Contains source code"}),` +`]}),` +`]}),` +`,e.jsx(n.li,{children:"package.json"}),` +`,e.jsx(n.li,{children:"tsconfig.json"}),` +`]}),` +`,e.jsx(n.h3,{id:"cli-commands",children:"CLI Commands"}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"clean"})," - Clean ",e.jsx(n.code,{children:"dist"})," (build) and ",e.jsx(n.code,{children:"docs"})," folders"]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"lint"})," - Run ESLint on ",e.jsx(n.code,{children:"src"})," directory to ensure source code adheres to coding standards"]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"docs"})," - Generate documentation - both Storybook and API reference"]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"docs:api"})," - Generate only API documentation"]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"docs:storybook"})," - Generate only Storybook documentation"]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"storybook"})," - Preview Storybook components"]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"build"})," - Build the project - lints, cleans, transpiles and generates documentation. Used for deployment."]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"dev"})," - Simple build and linking for local development"]}),` +`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"test"})," - Run tests"]}),` +`]}),` +`,e.jsx(n.h2,{id:"roadmap",children:"Roadmap"}),` +`,e.jsx(n.p,{children:"Contributors are free to pick up any of the following tasks or suggest new features."}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsx(n.li,{children:"Setup a bundled version of the project for CDN deployment"}),` +`]})]})}function u(r={}){const{wrapper:n}={...i(),...r.components};return n?e.jsx(n,{...r,children:e.jsx(s,{...r})}):s(r)}export{u as default}; diff --git a/docs/assets/Controller-DZoLtyi4.js b/docs/assets/Controller-DZoLtyi4.js new file mode 100644 index 0000000..72d6cdc --- /dev/null +++ b/docs/assets/Controller-DZoLtyi4.js @@ -0,0 +1,19 @@ +import{j as e}from"./jsx-runtime-DRTy3Uxn.js";import{useMDXComponents as t}from"./index-z5U8iC57.js";import{M as s,d as o}from"./index-iQvBBB1Z.js";import{None as c,Ui as l,Code as d}from"./Controller.stories-DG1QIQlB.js";import"./index-BBkUAzwr.js";import"./iframe-C3OM9s_A.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";import"./Simulation-7H4WISkI.js";import"./Universe-DrO6ncQG.js";function i(r){const n={code:"code",h2:"h2",pre:"pre",...t(),...r.components};return e.jsxs(e.Fragment,{children:[e.jsx(s,{title:"Visualize/Controller"}),` +`,e.jsx(n.h2,{id:"none-default",children:"None (default)"}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-js",children:`new Simulation(universe, {\r + controller: 'none'\r +}) +`})}),` +`,e.jsx("center",{children:e.jsx(o,{of:c,name:"None"})}),` +`,e.jsx(n.h2,{id:"ui",children:"Ui"}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-js",children:`new Simulation(universe, {\r + controller: 'ui'\r +}) +`})}),` +`,e.jsx("center",{children:e.jsx(o,{of:l,name:"Ui"})}),` +`,e.jsx(n.h2,{id:"code",children:"Code"}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-js",children:`new Simulation(universe, {\r + controller: 'code'\r +}) +`})}),` +`,e.jsx("center",{children:e.jsx(o,{of:d,name:"Code"})})]})}function w(r={}){const{wrapper:n}={...t(),...r.components};return n?e.jsx(n,{...r,children:e.jsx(i,{...r})}):i(r)}export{w as default}; diff --git a/docs/assets/Controller.stories-DG1QIQlB.js b/docs/assets/Controller.stories-DG1QIQlB.js new file mode 100644 index 0000000..d280f8c --- /dev/null +++ b/docs/assets/Controller.stories-DG1QIQlB.js @@ -0,0 +1,27 @@ +import{S as v}from"./Simulation-7H4WISkI.js";import{f as e}from"./Universe-DrO6ncQG.js";import"./jsx-runtime-DRTy3Uxn.js";import"./index-BBkUAzwr.js";const U={title:"Visualize/Controller",component:v,parameters:{layout:"centered",controls:{disable:!0}},tags:[],argTypes:{},args:{}},r={args:{storyName:"None",universe:[e],controller:"none"}},o={args:{storyName:"Ui",universe:[e],controller:"ui"}},n={args:{storyName:"Code",universe:[e],controller:"code",callback:s=>{const g=setInterval(()=>{s.setShowUniverse(e.label,!s.getShowUniverse(e.label))},500);return()=>{clearInterval(g)}}}};var a,t,i;r.parameters={...r.parameters,docs:{...(a=r.parameters)==null?void 0:a.docs,source:{originalSource:`{ + args: { + storyName: 'None', + universe: [fig8], + controller: 'none' + } +}`,...(i=(t=r.parameters)==null?void 0:t.docs)==null?void 0:i.source}}};var c,l,m;o.parameters={...o.parameters,docs:{...(c=o.parameters)==null?void 0:c.docs,source:{originalSource:`{ + args: { + storyName: 'Ui', + universe: [fig8], + controller: 'ui' + } +}`,...(m=(l=o.parameters)==null?void 0:l.docs)==null?void 0:m.source}}};var u,d,p;n.parameters={...n.parameters,docs:{...(u=n.parameters)==null?void 0:u.docs,source:{originalSource:`{ + args: { + storyName: 'Code', + universe: [fig8], + controller: 'code', + callback: sim => { + const id = setInterval(() => { + sim.setShowUniverse(fig8.label, !sim.getShowUniverse(fig8.label)); + }, 500); + return () => { + clearInterval(id); + }; + } + } +}`,...(p=(d=n.parameters)==null?void 0:d.docs)==null?void 0:p.source}}};const b=["None","Ui","Code"];export{n as Code,r as None,o as Ui,b as __namedExportsOrder,U as default}; diff --git a/docs/assets/Debug-cu0XayVm.js b/docs/assets/Debug-cu0XayVm.js new file mode 100644 index 0000000..cd2ee65 --- /dev/null +++ b/docs/assets/Debug-cu0XayVm.js @@ -0,0 +1,15 @@ +import{j as e}from"./jsx-runtime-DRTy3Uxn.js";import{useMDXComponents as i}from"./index-z5U8iC57.js";import{M as s,d as t}from"./index-iQvBBB1Z.js";import{DebugInfoOff as a,DebugInfoOn as m}from"./Debug.stories-C9fkCxNd.js";import"./index-BBkUAzwr.js";import"./iframe-C3OM9s_A.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";import"./Simulation-7H4WISkI.js";import"./Universe-DrO6ncQG.js";function r(o){const n={code:"code",h1:"h1",h2:"h2",p:"p",pre:"pre",...i(),...o.components};return e.jsxs(e.Fragment,{children:[e.jsx(s,{title:"Visualize/Debug Info"}),` +`,e.jsx(n.h1,{id:"debug-info",children:"Debug Info"}),` +`,e.jsxs(n.p,{children:["Passed as the ",e.jsx(n.code,{children:"showDebugInfo"})," property in the Simulation config. This will display debug information in the corner of the simulation canvas. Click on the pop-up to toggle between frames showing the frame rate, time taken for each frame and the total memory usage."]}),` +`,e.jsx(n.h2,{id:"off-default",children:"Off (default)"}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-js",children:`new Simulation(universe, {\r + showDebugInfo: false\r +}) +`})}),` +`,e.jsx("center",{children:e.jsx(t,{of:a,name:"Off"})}),` +`,e.jsx(n.h2,{id:"on",children:"On"}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-js",children:`new Simulation(universe, {\r + showDebugInfo: true\r +}) +`})}),` +`,e.jsx("center",{children:e.jsx(t,{of:m,name:"On"})})]})}function w(o={}){const{wrapper:n}={...i(),...o.components};return n?e.jsx(n,{...o,children:e.jsx(r,{...o})}):r(o)}export{w as default}; diff --git a/docs/assets/Debug.stories-C9fkCxNd.js b/docs/assets/Debug.stories-C9fkCxNd.js new file mode 100644 index 0000000..d2779d8 --- /dev/null +++ b/docs/assets/Debug.stories-C9fkCxNd.js @@ -0,0 +1,19 @@ +import{S as p}from"./Simulation-7H4WISkI.js";import{f as s}from"./Universe-DrO6ncQG.js";import"./jsx-runtime-DRTy3Uxn.js";import"./index-BBkUAzwr.js";const I={title:"Visualize/Debug Info",component:p,parameters:{layout:"centered",controls:{disable:!0}},tags:[],argTypes:{},args:{}},e={args:{storyName:"DebugInfoOn",universe:[s],showDebugInfo:!0}},r={args:{storyName:"DebugInfoOff",universe:[s],showDebugInfo:!1}},o={args:{storyName:"Ui",universe:[s],controller:"ui"}};var n,a,t;e.parameters={...e.parameters,docs:{...(n=e.parameters)==null?void 0:n.docs,source:{originalSource:`{ + args: { + storyName: 'DebugInfoOn', + universe: [fig8], + showDebugInfo: true + } +}`,...(t=(a=e.parameters)==null?void 0:a.docs)==null?void 0:t.source}}};var u,i,f;r.parameters={...r.parameters,docs:{...(u=r.parameters)==null?void 0:u.docs,source:{originalSource:`{ + args: { + storyName: 'DebugInfoOff', + universe: [fig8], + showDebugInfo: false + } +}`,...(f=(i=r.parameters)==null?void 0:i.docs)==null?void 0:f.source}}};var g,m,c;o.parameters={...o.parameters,docs:{...(g=o.parameters)==null?void 0:g.docs,source:{originalSource:`{ + args: { + storyName: 'Ui', + universe: [fig8], + controller: 'ui' + } +}`,...(c=(m=o.parameters)==null?void 0:m.docs)==null?void 0:c.source}}};const O=["DebugInfoOn","DebugInfoOff","Ui"];export{r as DebugInfoOff,e as DebugInfoOn,o as Ui,O as __namedExportsOrder,I as default}; diff --git a/docs/assets/Dimension-q3qUdgUC.js b/docs/assets/Dimension-q3qUdgUC.js new file mode 100644 index 0000000..34651fd --- /dev/null +++ b/docs/assets/Dimension-q3qUdgUC.js @@ -0,0 +1,15 @@ +import{j as n}from"./jsx-runtime-DRTy3Uxn.js";import{useMDXComponents as t}from"./index-z5U8iC57.js";import{M as s,d as i}from"./index-iQvBBB1Z.js";import{TwoD as m,ThreeD as c}from"./Dimension.stories-BWmrmXNV.js";import"./index-BBkUAzwr.js";import"./iframe-C3OM9s_A.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";import"./Simulation-7H4WISkI.js";import"./Universe-DrO6ncQG.js";function o(r){const e={code:"code",h1:"h1",h2:"h2",p:"p",pre:"pre",...t(),...r.components};return n.jsxs(n.Fragment,{children:[n.jsx(s,{title:"Visualize/Dimension"}),` +`,n.jsx(e.h1,{id:"dimension",children:"Dimension"}),` +`,n.jsx(e.p,{children:"TODO"}),` +`,n.jsx(e.h2,{id:"2d-default",children:"2D (default)"}),` +`,n.jsx(e.pre,{children:n.jsx(e.code,{className:"language-js",children:`new Simulation(universe, {\r + visType: '2D',\r +}) +`})}),` +`,n.jsx("center",{children:n.jsx(i,{of:m,name:"2D"})}),` +`,n.jsx(e.h2,{id:"3d",children:"3D"}),` +`,n.jsx(e.pre,{children:n.jsx(e.code,{className:"language-js",children:`new Simulation(universe, {\r + visType: '3D',\r +}) +`})}),` +`,n.jsx("center",{children:n.jsx(i,{of:c,name:"3D"})})]})}function T(r={}){const{wrapper:e}={...t(),...r.components};return e?n.jsx(e,{...r,children:n.jsx(o,{...r})}):o(r)}export{T as default}; diff --git a/docs/assets/Dimension.stories-BWmrmXNV.js b/docs/assets/Dimension.stories-BWmrmXNV.js new file mode 100644 index 0000000..c4ab16d --- /dev/null +++ b/docs/assets/Dimension.stories-BWmrmXNV.js @@ -0,0 +1,12 @@ +import{S as c}from"./Simulation-7H4WISkI.js";import{f as m}from"./Universe-DrO6ncQG.js";import"./jsx-runtime-DRTy3Uxn.js";import"./index-BBkUAzwr.js";const D={title:"Visualize/Dimension",component:c,parameters:{layout:"centered",controls:{disable:!0}},tags:[],argTypes:{},args:{}},e={args:{storyName:"TwoD",universe:[m]}},r={args:{storyName:"ThreeD",universe:[m],visType:"3D"}};var s,a,o;e.parameters={...e.parameters,docs:{...(s=e.parameters)==null?void 0:s.docs,source:{originalSource:`{ + args: { + storyName: 'TwoD', + universe: [fig8] + } +}`,...(o=(a=e.parameters)==null?void 0:a.docs)==null?void 0:o.source}}};var t,n,i;r.parameters={...r.parameters,docs:{...(t=r.parameters)==null?void 0:t.docs,source:{originalSource:`{ + args: { + storyName: 'ThreeD', + universe: [fig8], + visType: '3D' + } +}`,...(i=(n=r.parameters)==null?void 0:n.docs)==null?void 0:i.source}}};const T=["TwoD","ThreeD"];export{r as ThreeD,e as TwoD,T as __namedExportsOrder,D as default}; diff --git a/docs/assets/DocsRenderer-K4EAMTCU-BA-vEMEx.js b/docs/assets/DocsRenderer-K4EAMTCU-CsI0HbRn.js similarity index 90% rename from docs/assets/DocsRenderer-K4EAMTCU-BA-vEMEx.js rename to docs/assets/DocsRenderer-K4EAMTCU-CsI0HbRn.js index 3f94880..9eb3141 100644 --- a/docs/assets/DocsRenderer-K4EAMTCU-BA-vEMEx.js +++ b/docs/assets/DocsRenderer-K4EAMTCU-CsI0HbRn.js @@ -4,4 +4,4 @@ function __vite__mapDeps(indexes) { } return indexes.map((i) => __vite__mapDeps.viteFileDeps[i]) } -import{_ as p}from"./iframe-CQxbU3Lp.js";import{R as e,r as c}from"./index-BBkUAzwr.js";import{r as l,u}from"./react-18-B-OKcmzb.js";import{C as h,A as E,H as d,D as x}from"./index-Bd-WH8Nz.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";var _={code:h,a:E,...d},D=class extends c.Component{constructor(){super(...arguments),this.state={hasError:!1}}static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(t){let{showException:r}=this.props;r(t)}render(){let{hasError:t}=this.state,{children:r}=this.props;return t?null:e.createElement(e.Fragment,null,r)}},A=class{constructor(){this.render=async(t,r,o)=>{let n={..._,...r==null?void 0:r.components},s=x;return new Promise((m,a)=>{p(()=>import("./index-z5U8iC57.js"),__vite__mapDeps([0,1]),import.meta.url).then(({MDXProvider:i})=>l(e.createElement(D,{showException:a,key:Math.random()},e.createElement(i,{components:n},e.createElement(s,{context:t,docsParameter:r}))),o)).then(()=>m())})},this.unmount=t=>{u(t)}}};export{A as DocsRenderer,_ as defaultComponents}; +import{_ as p}from"./iframe-C3OM9s_A.js";import{R as e,r as c}from"./index-BBkUAzwr.js";import{r as l,u}from"./react-18-B-OKcmzb.js";import{C as h,A as E,H as d,D as x}from"./index-iQvBBB1Z.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";var _={code:h,a:E,...d},D=class extends c.Component{constructor(){super(...arguments),this.state={hasError:!1}}static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(t){let{showException:r}=this.props;r(t)}render(){let{hasError:t}=this.state,{children:r}=this.props;return t?null:e.createElement(e.Fragment,null,r)}},A=class{constructor(){this.render=async(t,r,o)=>{let n={..._,...r==null?void 0:r.components},s=x;return new Promise((m,a)=>{p(()=>import("./index-z5U8iC57.js"),__vite__mapDeps([0,1]),import.meta.url).then(({MDXProvider:i})=>l(e.createElement(D,{showException:a,key:Math.random()},e.createElement(i,{components:n},e.createElement(s,{context:t,docsParameter:r}))),o)).then(()=>m())})},this.unmount=t=>{u(t)}}};export{A as DocsRenderer,_ as defaultComponents}; diff --git a/docs/assets/Force-DULVW8v9.js b/docs/assets/Force-DULVW8v9.js new file mode 100644 index 0000000..ccb04e0 --- /dev/null +++ b/docs/assets/Force-DULVW8v9.js @@ -0,0 +1,101 @@ +import{j as e}from"./jsx-runtime-DRTy3Uxn.js";import{useMDXComponents as o}from"./index-z5U8iC57.js";import{M as t}from"./index-iQvBBB1Z.js";import"./index-BBkUAzwr.js";import"./iframe-C3OM9s_A.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";function i(r){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",ul:"ul",...o(),...r.components};return e.jsxs(e.Fragment,{children:[e.jsx(t,{title:"Define/Force"}),` +`,e.jsx(n.h1,{id:"force",children:"Force"}),` +`,e.jsx(n.p,{children:"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."}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`interface Force {\r + getForces(bodies: CelestialBody[]): Vector3[];\r +} +`})}),` +`,e.jsxs(n.p,{children:["Full API reference can be found ",e.jsx(n.a,{href:"https://source-academy.github.io/nbody/api/interfaces/Force.html",rel:"nofollow",children:"here"}),"."]}),` +`,e.jsxs(n.ul,{children:[` +`,e.jsx(n.li,{children:e.jsx(n.a,{href:"#inbuilt-forces",children:"Inbuilt Forces"})}),` +`,e.jsx(n.li,{children:e.jsx(n.a,{href:"#javascript",children:"Javascript"})}),` +`,e.jsx(n.li,{children:e.jsx(n.a,{href:"#typescript",children:"Typescript"})}),` +`]}),` +`,e.jsx(n.h2,{id:"inbuilt-forces",children:"Inbuilt Forces"}),` +`,e.jsx(n.h3,{id:"gravity",children:"Gravity"}),` +`,e.jsxs(n.p,{children:["Create a ",e.jsx(n.a,{href:"https://en.wikipedia.org/wiki/Newton%27s_law_of_universal_gravitation",rel:"nofollow",children:"newtonian gravitational"})," force object with a gravitational constant of ",e.jsx(n.code,{children:"6.674e-11"}),". You can also pass in a custom gravitational constant."]}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-javascript",children:`new Gravity();\r +new Gravity(10); +`})}),` +`,e.jsx(n.h3,{id:"centripetal-force",children:"Centripetal Force"}),` +`,e.jsxs(n.p,{children:["Create a ",e.jsx(n.a,{href:"https://en.wikipedia.org/wiki/Centripetal_force",rel:"nofollow",children:"centripetal"})," force object with a center of rotation. Default center is ",e.jsx(n.code,{children:"(0, 0, 0)"}),"."]}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-javascript",children:`new CentripetalForce();\r +new CentripetalForce(new Vector3(x, y, z)); +`})}),` +`,e.jsx(n.h3,{id:"combined-force",children:"Combined Force"}),` +`,e.jsx(n.p,{children:"Create a force object that is a result of additively combining multiple forces acting on a system of bodies."}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-javascript",children:`new CombinedForce([new Gravity(), new CentripetalForce()]); +`})}),` +`,e.jsx(n.h3,{id:"lambda-force",children:"Lambda Force"}),` +`,e.jsxs(n.p,{children:["Create a force object that uses a ",e.jsx(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions",rel:"nofollow",children:"lambda/arrow"})," function to calculate forces."]}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-javascript",children:`new LambdaForce((bodies) => {\r + return bodies.map(body => new Vector3(0, 0, 0)); // zero force\r +}); +`})}),` +`,e.jsx(n.h2,{id:"javascript",children:"Javascript"}),` +`,e.jsx(n.p,{children:"You can define and configure your own force object in javascript with a getForces method as follows"}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-javascript",children:`// gravitational constant\r +const G = 6.67430e-11;\r +\r +// define your own newtonian gravitational force\r +const gravity = {\r + // must contain a getForces method\r + getForces(bodies) {\r + let n = bodies.length;\r + let ans = [];\r + for (let i = 0; i < n; i++) {\r + ans.push(new Vector3(0, 0, 0));\r + }\r + for (let i = 0; i < n; i++) {\r + for (let j = i + 1; j < n; j++) {\r + let currForce = this.calcNewtonian(bodies[i], bodies[j]);\r + ans[i].add(currForce);\r + ans[j].sub(currForce);\r + }\r + }\r + return ans;\r + },\r + // helper function to calculate force between two bodies\r + calcNewtonian(a, b) {\r + let distSq = a.position.distanceToSquared(b.position);\r + let forceVal = (G * a.mass * b.mass) / distSq;\r + return b.position\r + .clone()\r + .sub(a.position)\r + .normalize()\r + .multiplyScalar(forceVal);\r + }\r +} +`})}),` +`,e.jsx(n.h2,{id:"typescript",children:"Typescript"}),` +`,e.jsx(n.p,{children:"You can define and configure your own force object in typescript by implementing the Force interface as follows"}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`class Gravity implements Force {\r + readonly G: number = 6.674e-11;\r +\r + getForces(bodies: CelestialBody[]): Vector3[] {\r + let n = bodies.length;\r + let ans: Vector3[] = [];\r + for (let i = 0; i < n; i++) {\r + ans.push(new Vector3(0, 0, 0));\r + }\r + for (let i = 0; i < n; i++) {\r + for (let j = i + 1; j < n; j++) {\r + let currForce = this.calcNewtonian(bodies[i], bodies[j]);\r + ans[i].add(currForce);\r + ans[j].sub(currForce);\r + }\r + }\r + return ans;\r + }\r +\r + private calcNewtonian(a: CelestialBody, b: CelestialBody): Vector3 {\r + let distSq = a.position.distanceToSquared(b.position);\r + let forceVal = (this.G * a.mass * b.mass) / distSq;\r + return b.position\r + .clone()\r + .sub(a.position)\r + .normalize()\r + .multiplyScalar(forceVal);\r + }\r +} +`})})]})}function u(r={}){const{wrapper:n}={...o(),...r.components};return n?e.jsx(n,{...r,children:e.jsx(i,{...r})}):i(r)}export{u as default}; diff --git a/docs/assets/HorseshoeOrbit-C7UQkWZK.js b/docs/assets/HorseshoeOrbit-C7UQkWZK.js new file mode 100644 index 0000000..e02c772 --- /dev/null +++ b/docs/assets/HorseshoeOrbit-C7UQkWZK.js @@ -0,0 +1,3 @@ +import{j as t}from"./jsx-runtime-DRTy3Uxn.js";import{useMDXComponents as n}from"./index-z5U8iC57.js";import{M as s}from"./index-iQvBBB1Z.js";import"./HorseshoeOrbit.stories-BMFTqkxu.js";import"./index-BBkUAzwr.js";import"./iframe-C3OM9s_A.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";import"./Simulation-7H4WISkI.js";import"./Transformation-KUb0IKcR.js";function e(o){const r={h1:"h1",p:"p",...n(),...o.components};return t.jsxs(t.Fragment,{children:[t.jsx(s,{title:"Showcase/HorseshoeOrbit/Intro"}),` +`,t.jsx(r.h1,{id:"horseshoe-orbits",children:"Horseshoe Orbits"}),` +`,t.jsx(r.p,{children:"TODO"})]})}function M(o={}){const{wrapper:r}={...n(),...o.components};return r?t.jsx(r,{...o,children:t.jsx(e,{...o})}):e(o)}export{M as default}; diff --git a/docs/assets/HorseshoeOrbit.stories-BMFTqkxu.js b/docs/assets/HorseshoeOrbit.stories-BMFTqkxu.js new file mode 100644 index 0000000..5223a6d --- /dev/null +++ b/docs/assets/HorseshoeOrbit.stories-BMFTqkxu.js @@ -0,0 +1,12 @@ +import{a,C as o,V as e,U as i,R as w,S as m}from"./Simulation-7H4WISkI.js";import{R as c,C as u,P as p}from"./Transformation-KUb0IKcR.js";import"./jsx-runtime-DRTy3Uxn.js";import"./index-BBkUAzwr.js";const l=new a([new o("Sun",19885e26,new e(0,0,0),new e(0,0,0),new e(0,0,0)),new o("Earth",597219e19,new e(-248109932596539e-4,1449948612736719e-4,-8215203670851886e-9),new e(-29841.46365518679,-5126.262286859617,1.184224839788195),new e(0,0,0)),new o("YORP",1,new e(1789598196203594e-4,467757011067789e-4,5131735873924753e-6),new e(-5641.374152889482,22817.8307950743,-65.07224186314708),new e(0,0,0))]),h=new i({label:"54509 YORP",currState:new c(new e(1,0,0),Math.PI/2).transform(l.clone()),simFunc:new w,color:["#FDB813","#287AB8","#767676"],transformations:[new u,new p(new e(1,0,0),1)]}),d={yorp:h},g={title:"Showcase/Horseshoe Orbit",component:m,parameters:{layout:"centered",controls:{disable:!0}},tags:[],argTypes:{},args:{}},r={args:{storyName:"Horseshoe Orbit YORP",universe:[d.yorp],showDebugInfo:!0,controller:"ui",visType:"3D",width:800,speed:1e7,showTrails:!0}};var s,n,t;r.parameters={...r.parameters,docs:{...(s=r.parameters)==null?void 0:s.docs,source:{originalSource:`{ + args: { + storyName: "Horseshoe Orbit YORP", + universe: [horseshoe.yorp], + showDebugInfo: true, + controller: 'ui', + visType: '3D', + width: 800, + speed: 10000000, + showTrails: true + } +}`,...(t=(n=r.parameters)==null?void 0:n.docs)==null?void 0:t.source}}};const P=["YORP"];export{r as YORP,P as __namedExportsOrder,g as default}; diff --git a/docs/assets/Install-DJ5cHZNu.js b/docs/assets/Install-BXrzk_ma.js similarity index 51% rename from docs/assets/Install-DJ5cHZNu.js rename to docs/assets/Install-BXrzk_ma.js index b8261d3..9883a56 100644 --- a/docs/assets/Install-DJ5cHZNu.js +++ b/docs/assets/Install-BXrzk_ma.js @@ -1,5 +1,6 @@ -import{j as e}from"./jsx-runtime-DRTy3Uxn.js";import{useMDXComponents as s}from"./index-z5U8iC57.js";import{M as l}from"./index-Bd-WH8Nz.js";import"./index-BBkUAzwr.js";import"./iframe-CQxbU3Lp.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";function o(t){const n={a:"a",code:"code",h1:"h1",p:"p",pre:"pre",...s(),...t.components};return e.jsxs(e.Fragment,{children:[e.jsx(l,{title:"Getting Started/Installation"}),` -`,e.jsx(n.h1,{id:"installation-guide",children:"Installation guide"}),` +import{j as e}from"./jsx-runtime-DRTy3Uxn.js";import{useMDXComponents as s}from"./index-z5U8iC57.js";import{M as r}from"./index-iQvBBB1Z.js";import"./index-BBkUAzwr.js";import"./iframe-C3OM9s_A.js";import"../sb-preview/runtime.js";import"./index-PqR-_bA4.js";import"./index-DrlA5mbP.js";import"./index-DrFu-skq.js";function o(t){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",p:"p",pre:"pre",strong:"strong",...s(),...t.components};return e.jsxs(e.Fragment,{children:[e.jsx(r,{title:"Installation"}),` +`,e.jsx(n.h1,{id:"installation",children:"Installation"}),` +`,e.jsx(n.h2,{id:"via-npm-registry",children:"Via npm registry"}),` `,e.jsxs(n.p,{children:["nbody is available on the npm registry as ",e.jsx(n.a,{href:"https://www.npmjs.com/package/nbody",rel:"nofollow",children:"nbody"}),". Simply follow the following install commands based on your package manager. Since nbody relies on ",e.jsx(n.a,{href:"https://threejs.org/",rel:"nofollow",children:"three"})," and ",e.jsx(n.a,{href:"https://plotly.com/javascript/",rel:"nofollow",children:"Plotly.js"})," for visualization of the simulations, they have to be installed alongside nbody."]}),` `,e.jsx(n.p,{children:"If you are using Typescript, you may also have to install type definitions as well."}),` `,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-bash",children:`npm install nbody three plotly.js-dist\r @@ -8,4 +9,11 @@ npm install --save-dev @types/three @types/plotly.js `,e.jsx(n.p,{children:"or"}),` `,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-bash",children:`yarn add nbody three plotly.js-dist\r yarn add -D @types/three @types/plotly.js +`})}),` +`,e.jsx(n.h2,{id:"via-cdn",children:"Via CDN"}),` +`,e.jsxs(n.blockquote,{children:[` +`,e.jsxs(n.p,{children:[e.jsx(n.strong,{children:"WARNING"}),": Using nbody via the CDN may not work as intended as peer dependencies of three and plotly.js would not be accessible to nbody during runtime. A bundled version is in the roadmap for future development - consider ",e.jsx(n.a,{href:"?path=/docs/getting-started-contribute--docs",children:"contributing"}),"."]}),` +`]}),` +`,e.jsx(n.p,{children:"nbody is available via the unpkg CDN as follows."}),` +`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-html",children:` - + diff --git a/docs/index.html b/docs/index.html index 459dbe9..87d0246 100644 --- a/docs/index.html +++ b/docs/index.html @@ -75,6 +75,7 @@ window['DOCS_OPTIONS'] = { + "docsMode": true, "defaultName": "Docs", "autodocs": "tag" }; diff --git a/docs/index.json b/docs/index.json index fa7033e..5fb2b22 100644 --- a/docs/index.json +++ b/docs/index.json @@ -1 +1 @@ -{"v":4,"entries":{"getting-started-nbody--docs":{"id":"getting-started-nbody--docs","title":"Getting started/nbody","name":"Docs","importPath":"./.storybook/stories/Nbody.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"usage-celestialbody--docs":{"id":"usage-celestialbody--docs","title":"Usage/CelestialBody","name":"Docs","importPath":"./.storybook/stories/Usage/CelestialBody.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"examples-simulation--docs":{"id":"examples-simulation--docs","title":"Examples/Simulation","name":"Docs","importPath":"./.storybook/stories/Examples/Simulation.mdx","storiesImports":["./.storybook/stories/Examples/Simulation.stories.tsx"],"type":"docs","tags":["attached-mdx","docs"]},"examples-simulation--two-dim":{"type":"story","id":"examples-simulation--two-dim","name":"Two Dim","title":"Examples/Simulation","importPath":"./.storybook/stories/Examples/Simulation.stories.tsx","tags":["story"]},"examples-simulation--three-dim":{"type":"story","id":"examples-simulation--three-dim","name":"Three Dim","title":"Examples/Simulation","importPath":"./.storybook/stories/Examples/Simulation.stories.tsx","tags":["story"]},"getting-started-installation--docs":{"id":"getting-started-installation--docs","title":"Getting Started/Installation","name":"Docs","importPath":"./.storybook/stories/Install.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"getting-started-integration--docs":{"id":"getting-started-integration--docs","title":"Getting Started/Integration","name":"Docs","importPath":"./.storybook/stories/Integration.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]}}} +{"v":4,"entries":{"nbody--docs":{"id":"nbody--docs","title":"nbody","name":"Docs","importPath":"./.storybook/stories/Nbody.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"installation--docs":{"id":"installation--docs","title":"Installation","name":"Docs","importPath":"./.storybook/stories/Install.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"quick-start--docs":{"id":"quick-start--docs","title":"Quick Start","name":"Docs","importPath":"./.storybook/stories/QuickStart.mdx","storiesImports":["./.storybook/stories/Visualize/Dimension/Dimension.stories.tsx"],"type":"docs","tags":["unattached-mdx","docs"]},"integration--docs":{"id":"integration--docs","title":"Integration","name":"Docs","importPath":"./.storybook/stories/Integration.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"contribute--docs":{"id":"contribute--docs","title":"Contribute","name":"Docs","importPath":"./.storybook/stories/Contribute.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"define-intro--docs":{"id":"define-intro--docs","title":"Define/Intro","name":"Docs","importPath":"./.storybook/stories/Define/Intro.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"define-force--docs":{"id":"define-force--docs","title":"Define/Force","name":"Docs","importPath":"./.storybook/stories/Define/Force.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"define-simulate-function--docs":{"id":"define-simulate-function--docs","title":"Define/Simulate Function","name":"Docs","importPath":"./.storybook/stories/Define/SimulateFunction.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"define-transformation--docs":{"id":"define-transformation--docs","title":"Define/Transformation","name":"Docs","importPath":"./.storybook/stories/Define/Transformation.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"visualize-intro--docs":{"id":"visualize-intro--docs","title":"Visualize/Intro","name":"Docs","importPath":"./.storybook/stories/Visualize/Intro.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"visualize-dimension--docs":{"id":"visualize-dimension--docs","title":"Visualize/Dimension","name":"Docs","importPath":"./.storybook/stories/Visualize/Dimension/Dimension.mdx","storiesImports":["./.storybook/stories/Visualize/Dimension/Dimension.stories.tsx"],"type":"docs","tags":["unattached-mdx","docs"]},"visualize-dimension--two-d":{"type":"story","id":"visualize-dimension--two-d","name":"Two D","title":"Visualize/Dimension","importPath":"./.storybook/stories/Visualize/Dimension/Dimension.stories.tsx","tags":["story"]},"visualize-dimension--three-d":{"type":"story","id":"visualize-dimension--three-d","name":"Three D","title":"Visualize/Dimension","importPath":"./.storybook/stories/Visualize/Dimension/Dimension.stories.tsx","tags":["story"]},"visualize-multiverse--docs":{"id":"visualize-multiverse--docs","title":"Visualize/Multiverse","name":"Docs","importPath":"./.storybook/stories/Visualize/Multiverse/Multiverse.mdx","storiesImports":["./.storybook/stories/Visualize/Multiverse/Multiverse.stories.tsx"],"type":"docs","tags":["unattached-mdx","docs"]},"visualize-multiverse--single-universe":{"type":"story","id":"visualize-multiverse--single-universe","name":"Single Universe","title":"Visualize/Multiverse","importPath":"./.storybook/stories/Visualize/Multiverse/Multiverse.stories.tsx","tags":["story"]},"visualize-multiverse--multiverse":{"type":"story","id":"visualize-multiverse--multiverse","name":"Multiverse","title":"Visualize/Multiverse","importPath":"./.storybook/stories/Visualize/Multiverse/Multiverse.stories.tsx","tags":["story"]},"visualize-multiverse--multiverse-3-d":{"type":"story","id":"visualize-multiverse--multiverse-3-d","name":"Multiverse 3 D","title":"Visualize/Multiverse","importPath":"./.storybook/stories/Visualize/Multiverse/Multiverse.stories.tsx","tags":["story"]},"visualize-record--docs":{"id":"visualize-record--docs","title":"Visualize/Record","name":"Docs","importPath":"./.storybook/stories/Visualize/Record/Record.mdx","storiesImports":["./.storybook/stories/Visualize/Record/Record.stories.tsx"],"type":"docs","tags":["unattached-mdx","docs"]},"visualize-record--real-time":{"type":"story","id":"visualize-record--real-time","name":"Real Time","title":"Visualize/Record","importPath":"./.storybook/stories/Visualize/Record/Record.stories.tsx","tags":["story"]},"visualize-record--recorded":{"type":"story","id":"visualize-record--recorded","name":"Recorded","title":"Visualize/Record","importPath":"./.storybook/stories/Visualize/Record/Record.stories.tsx","tags":["story"]},"visualize-record--recorded-looped":{"type":"story","id":"visualize-record--recorded-looped","name":"Recorded Looped","title":"Visualize/Record","importPath":"./.storybook/stories/Visualize/Record/Record.stories.tsx","tags":["story"]},"visualize-controller--docs":{"id":"visualize-controller--docs","title":"Visualize/Controller","name":"Docs","importPath":"./.storybook/stories/Visualize/Controller/Controller.mdx","storiesImports":["./.storybook/stories/Visualize/Controller/Controller.stories.tsx"],"type":"docs","tags":["unattached-mdx","docs"]},"visualize-controller--none":{"type":"story","id":"visualize-controller--none","name":"None","title":"Visualize/Controller","importPath":"./.storybook/stories/Visualize/Controller/Controller.stories.tsx","tags":["story"]},"visualize-controller--ui":{"type":"story","id":"visualize-controller--ui","name":"Ui","title":"Visualize/Controller","importPath":"./.storybook/stories/Visualize/Controller/Controller.stories.tsx","tags":["story"]},"visualize-controller--code":{"type":"story","id":"visualize-controller--code","name":"Code","title":"Visualize/Controller","importPath":"./.storybook/stories/Visualize/Controller/Controller.stories.tsx","tags":["story"]},"visualize-trails--docs":{"id":"visualize-trails--docs","title":"Visualize/Trails","name":"Docs","importPath":"./.storybook/stories/Visualize/Trails/Trails.mdx","storiesImports":["./.storybook/stories/Visualize/Trails/Trails.stories.tsx"],"type":"docs","tags":["unattached-mdx","docs"]},"visualize-trails--show-trails-off":{"type":"story","id":"visualize-trails--show-trails-off","name":"Show Trails Off","title":"Visualize/Trails","importPath":"./.storybook/stories/Visualize/Trails/Trails.stories.tsx","tags":["story"]},"visualize-trails--show-trails-on":{"type":"story","id":"visualize-trails--show-trails-on","name":"Show Trails On","title":"Visualize/Trails","importPath":"./.storybook/stories/Visualize/Trails/Trails.stories.tsx","tags":["story"]},"visualize-debug-info--docs":{"id":"visualize-debug-info--docs","title":"Visualize/Debug Info","name":"Docs","importPath":"./.storybook/stories/Visualize/Debug Info/Debug.mdx","storiesImports":["./.storybook/stories/Visualize/Debug Info/Debug.stories.tsx"],"type":"docs","tags":["unattached-mdx","docs"]},"visualize-debug-info--debug-info-on":{"type":"story","id":"visualize-debug-info--debug-info-on","name":"Debug Info On","title":"Visualize/Debug Info","importPath":"./.storybook/stories/Visualize/Debug Info/Debug.stories.tsx","tags":["story"]},"visualize-debug-info--debug-info-off":{"type":"story","id":"visualize-debug-info--debug-info-off","name":"Debug Info Off","title":"Visualize/Debug Info","importPath":"./.storybook/stories/Visualize/Debug Info/Debug.stories.tsx","tags":["story"]},"visualize-debug-info--ui":{"type":"story","id":"visualize-debug-info--ui","name":"Ui","title":"Visualize/Debug Info","importPath":"./.storybook/stories/Visualize/Debug Info/Debug.stories.tsx","tags":["story"]},"showcase-intro--docs":{"id":"showcase-intro--docs","title":"Showcase/Intro","name":"Docs","importPath":"./.storybook/stories/Showcase/Intro.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"showcase-analemma--sun-earth-analemma":{"type":"story","id":"showcase-analemma--sun-earth-analemma","name":"Sun Earth Analemma","title":"Showcase/Analemma","importPath":"./.storybook/stories/Showcase/Analemma/Analemma.stories.tsx","tags":["story"]},"showcase-analemma-intro--docs":{"id":"showcase-analemma-intro--docs","title":"Showcase/Analemma/Intro","name":"Docs","importPath":"./.storybook/stories/Showcase/Analemma/Analemma.mdx","storiesImports":[],"type":"docs","tags":["unattached-mdx","docs"]},"showcase-analemma-sun-earth--docs":{"id":"showcase-analemma-sun-earth--docs","title":"Showcase/Analemma/Sun-Earth","name":"Docs","importPath":"./.storybook/stories/Showcase/Analemma/Sun-Earth.mdx","storiesImports":["./.storybook/stories/Showcase/Analemma/Analemma.stories.tsx"],"type":"docs","tags":["unattached-mdx","docs"]},"showcase-horseshoeorbit-intro--docs":{"id":"showcase-horseshoeorbit-intro--docs","title":"Showcase/HorseshoeOrbit/Intro","name":"Docs","importPath":"./.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.mdx","storiesImports":["./.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.stories.tsx"],"type":"docs","tags":["unattached-mdx","docs"]},"showcase-horseshoeorbit-54509-yorp--docs":{"id":"showcase-horseshoeorbit-54509-yorp--docs","title":"Showcase/HorseshoeOrbit/54509 YORP","name":"Docs","importPath":"./.storybook/stories/Showcase/HorseshoeOrbit/54509 YORP.mdx","storiesImports":["./.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.stories.tsx"],"type":"docs","tags":["unattached-mdx","docs"]},"showcase-solarsystem--docs":{"id":"showcase-solarsystem--docs","title":"Showcase/SolarSystem","name":"Docs","importPath":"./.storybook/stories/Showcase/SolarSystem/SolarSystem.mdx","storiesImports":["./.storybook/stories/Showcase/SolarSystem/SolarSystem.stories.tsx"],"type":"docs","tags":["unattached-mdx","docs"]},"showcase-horseshoe-orbit--yorp":{"type":"story","id":"showcase-horseshoe-orbit--yorp","name":"YORP","title":"Showcase/Horseshoe Orbit","importPath":"./.storybook/stories/Showcase/HorseshoeOrbit/HorseshoeOrbit.stories.tsx","tags":["story"]},"showcase-solar-system--solar-system":{"type":"story","id":"showcase-solar-system--solar-system","name":"Solar System","title":"Showcase/Solar System","importPath":"./.storybook/stories/Showcase/SolarSystem/SolarSystem.stories.tsx","tags":["story"]}}} diff --git a/docs/project.json b/docs/project.json index aee1f97..2fed57f 100644 --- a/docs/project.json +++ b/docs/project.json @@ -1 +1 @@ -{"generatedAt":1712440985915,"hasCustomBabel":false,"hasCustomWebpack":false,"hasStaticDirs":false,"hasStorybookEslint":true,"refCount":0,"packageManager":{"type":"yarn","version":"1.22.22"},"preview":{"usesGlobals":false},"framework":{"name":"@storybook/react-vite","options":{}},"builder":"@storybook/builder-vite","renderer":"@storybook/react","storybookVersion":"8.0.6","storybookVersionSpecifier":"^8.0.6","language":"typescript","storybookPackages":{"@chromatic-com/storybook":{"version":"1.3.1"},"@storybook/addon-interactions":{"version":"8.0.6"},"@storybook/addon-links":{"version":"8.0.6"},"@storybook/addon-onboarding":{"version":"8.0.6"},"@storybook/blocks":{"version":"8.0.6"},"@storybook/react":{"version":"8.0.6"},"@storybook/react-vite":{"version":"8.0.6"},"@storybook/test":{"version":"8.0.6"},"eslint-plugin-storybook":{"version":"0.8.0"},"storybook":{"version":"8.0.6"}},"addons":{"@storybook/addon-essentials":{"options":{"actions":false},"version":"8.0.6"}}} +{"generatedAt":1712562599303,"hasCustomBabel":false,"hasCustomWebpack":false,"hasStaticDirs":false,"hasStorybookEslint":true,"refCount":0,"packageManager":{"type":"yarn","version":"1.22.22"},"preview":{"usesGlobals":false},"framework":{"name":"@storybook/react-vite","options":{}},"builder":"@storybook/builder-vite","renderer":"@storybook/react","storybookVersion":"8.0.6","storybookVersionSpecifier":"^8.0.6","language":"typescript","storybookPackages":{"@chromatic-com/storybook":{"version":"1.3.1"},"@storybook/addon-interactions":{"version":"8.0.6"},"@storybook/addon-links":{"version":"8.0.6"},"@storybook/addon-onboarding":{"version":"8.0.6"},"@storybook/blocks":{"version":"8.0.6"},"@storybook/react":{"version":"8.0.6"},"@storybook/react-vite":{"version":"8.0.6"},"@storybook/test":{"version":"8.0.6"},"eslint-plugin-storybook":{"version":"0.8.0"},"storybook":{"version":"8.0.6"}},"addons":{"@storybook/addon-essentials":{"options":{"actions":false},"version":"8.0.6"}}} diff --git a/package.json b/package.json index 0794759..2c63cf4 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "lint": "eslint --config src/.eslintrc.cjs --quiet .", "docs": "yarn docs:storybook && yarn docs:api", "docs:api": "typedoc", - "docs:storybook": "storybook build -o docs", + "docs:storybook": "storybook build --docs -o docs", "storybook": "storybook dev -p 6006", "build": "yarn lint && yarn clean && tsc && yarn docs", "dev": "yarn clean && tsc && yarn unlink && yarn link", diff --git a/src/index.ts b/src/index.ts index 7af5167..39205a5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,7 +23,10 @@ import { State } from './State'; import { BodyCenterTransformation, - CoMTransformation, RotateTransformation, + CoMTransformation, + PinTransformation, + RotateTransformation, + TimedRotateTransformation, } from './library/Transformation'; import { LambdaTransformation, type Transformation } from './Transformation'; @@ -38,10 +41,8 @@ import { Vector3 } from 'three'; export { BodyCenterTransformation, CelestialBody, CentripetalForce, - CombinedForce, CoMTransformation, ExplicitEulerSim, Gravity, LambdaForce, LambdaSim, LambdaTransformation, RealTimeVisualizer, - RealTimeVisualizer3D, - RecordingVisualizer, + CombinedForce, CoMTransformation, ExplicitEulerSim, Gravity, LambdaForce, LambdaSim, LambdaTransformation, PinTransformation, RealTimeVisualizer, + RealTimeVisualizer3D, RecordingVisualizer, RecordingVisualizer3D, - RotateTransformation, RungeKutta4Sim, - SemiImplicitEulerSim, Simulation, State, Universe, Vector3, VelocityVerletSim, type ControllerType, type Force, type SimulateFunction, type Transformation, type UniverseConfig, type VisType, + RotateTransformation, RungeKutta4Sim, SemiImplicitEulerSim, Simulation, State, TimedRotateTransformation, Universe, Vector3, VelocityVerletSim, type ControllerType, type Force, type SimulateFunction, type Transformation, type UniverseConfig, type VisType, }; diff --git a/src/library/SimulateFunction.ts b/src/library/SimulateFunction.ts index 74334d6..c1001f8 100644 --- a/src/library/SimulateFunction.ts +++ b/src/library/SimulateFunction.ts @@ -3,6 +3,7 @@ import { type CelestialBody } from '../CelestialBody'; import { type Force } from '../Force'; import { type SimulateFunction } from '../SimulateFunction'; import { State } from '../State'; +import { Gravity } from './Force'; // export class VerletSim implements SimulateFunction { // forceCalculator: Force; @@ -105,7 +106,7 @@ export class VelocityVerletSim implements SimulateFunction { * Create a new VelocityVerletSim with the provided force calculator, which is invoked on every simulation step. * @param forceCalculator force calculator. */ - constructor(forceCalculator: Force) { + constructor(forceCalculator: Force = new Gravity()) { this.forceCalculator = forceCalculator; } @@ -184,7 +185,7 @@ export class ExplicitEulerSim implements SimulateFunction { * Create a new ExplicitEulerSim with the provided force calculator, which is invoked on every simulation step. * @param force force calculator. */ - constructor(force: Force) { + constructor(force: Force = new Gravity()) { this.force = force; } @@ -241,7 +242,7 @@ export class SemiImplicitEulerSim implements SimulateFunction { * Create a new SemiImplicitEulerSim with the provided force calculator, which is invoked on every simulation step. * @param force force calculator. */ - constructor(force: Force) { + constructor(force: Force = new Gravity()) { this.force = force; } @@ -320,7 +321,7 @@ export class RungeKutta4Sim implements SimulateFunction { * @param force force calculator. * @param weights weights for weighted average. */ - constructor(force: Force, weights: number[]) { + constructor(force: Force = new Gravity(), weights: number[] = [1, 2, 2, 1]) { this.force = force; if (weights.length !== 4) { throw new Error('Weights for RK4 must be of length 4'); diff --git a/src/library/Transformation.ts b/src/library/Transformation.ts index 5d26482..f37c3ff 100644 --- a/src/library/Transformation.ts +++ b/src/library/Transformation.ts @@ -3,17 +3,27 @@ import { type State } from '../State'; import { type Transformation } from '../Transformation'; /** - * Frame of reference transformation to the center of the first body in the system. + * Frame of reference transformation to the center of body i in the system. * @category Transformations */ export class BodyCenterTransformation implements Transformation { + readonly index: number; + + /** + * Create a new BodyCenterTransformer. + * @param index index of the body to transform to. + */ + constructor(index: number) { + this.index = index; + } + /** * Transform the frame of reference to the center of the first body in the system. * @param state state to transform. * @returns transformed state. */ transform(state: State): State { - const transform = state.bodies[0].position.clone(); + const transform = state.bodies[this.index].position.clone(); state.bodies.forEach((b) => { b.position.sub(transform); }); @@ -80,23 +90,73 @@ export class RotateTransformation implements Transformation { } } -// export class PinTransformer implements Transformer { -// readonly axis: Vector3; -// readonly index: number; +/** + * Frame of reference transformation to a pin body i to the given axis. + */ +export class PinTransformation implements Transformation { + readonly axis: Vector3; + readonly index: number; + + /** + * Create a new PinTransformer. + * @param axis axis to pin to. + * @param index index of the body to pin. + */ + constructor(axis: Vector3, index: number) { + this.axis = axis; + this.index = index; + } + + /** + * Transform the frame of reference to a pin body i to the given axis. + * @param state state to transform. + * @returns transformed state. + */ + transform(state: State): State { + const angle = state.bodies[this.index].position.clone() + .angleTo(this.axis.clone()); + const pivot = state.bodies[this.index].position.clone() + .cross(this.axis.clone()) + .normalize(); + state.bodies.forEach((b) => { + b.position.applyAxisAngle(pivot.clone(), angle); + b.velocity.applyAxisAngle(pivot.clone(), angle); + b.acceleration.applyAxisAngle(pivot.clone(), angle); + }); + return state; + } +} + +/** + * Frame of reference transformation to rotate around an axis by 360 degrees in a given time. + */ +export class TimedRotateTransformation implements Transformation { + readonly axis: Vector3; + readonly revolutionTime: number; -// constructor(axis: Vector3, index: number) { -// this.axis = axis; -// this.index = index -// } + /** + * Create a new TimedRotateTransformer. + * @param axis axis to rotate around. + * @param revolutionTime time in seconds for one full revolution. + */ + constructor(axis: Vector3, revolutionTime: number) { + this.axis = axis; + this.revolutionTime = revolutionTime; + } -// transform(state: State): State { -// const angle = state.bodies[this.index].position.clone().angleTo(this.axis); -// const pivot = state.bodies[this.index].position.clone().cross(this.axis.clone()).normalize(); -// state.bodies.forEach((b) => { -// b.position.applyAxisAngle(pivot.clone(), angle); -// b.velocity.applyAxisAngle(pivot.clone(), angle); -// b.acceleration.applyAxisAngle(pivot.clone(), angle); -// }); -// return state; -// } -// } + /** + * Transform the frame of reference to rotate around an axis by an angle determined by the time elapsed. + * @param state state to transform. + * @param deltaT time elapsed. + * @returns transformed state. + */ + transform(state: State, deltaT: number): State { + const angle = -(deltaT / this.revolutionTime) * Math.PI * 2; + state.bodies.forEach((b) => { + b.position.applyAxisAngle(this.axis, angle); + b.velocity.applyAxisAngle(this.axis, angle); + b.acceleration.applyAxisAngle(this.axis, angle); + }); + return state; + } +} diff --git a/src/library/Visualizer.ts b/src/library/Visualizer.ts index 61b8f13..a2450c4 100644 --- a/src/library/Visualizer.ts +++ b/src/library/Visualizer.ts @@ -333,17 +333,19 @@ export class RealTimeVisualizer implements Visualizer { * Stop the simulation and visualization. */ stop(): void { - console.log('stopping in viz'); if (this.animationId === null) { return; } cancelAnimationFrame(this.animationId); - Plotly.purge(this.divId); this.divId = ''; this.universeTrails.forEach((ut) => { ut.popAllTrails(); }); this.universeTrails = []; + try { + Plotly.purge(this.divId); + } catch (_) { + } } } @@ -436,7 +438,10 @@ class ThreeUniverseTrail { */ export class RealTimeVisualizer3D implements Visualizer { animationId: number | null = null; - renderer: THREE.WebGLRenderer | null = null; + /** + * Clear the visualization. + */ + clear: () => void = () => { }; simulation: Simulation; scene?: THREE.Scene; universeTrails: ThreeUniverseTrail[] = []; @@ -525,10 +530,10 @@ export class RealTimeVisualizer3D implements Visualizer { ); camera.position.set(0, 0, Math.max(width, height)); - this.renderer = new THREE.WebGLRenderer(); - this.renderer.setSize(width, height); - this.renderer.autoClear = false; - element.appendChild(this.renderer.domElement); + const renderer = new THREE.WebGLRenderer(); + renderer.setSize(width, height); + renderer.autoClear = false; + element.appendChild(renderer.domElement); let stats: Stats | undefined; if (this.simulation.showDebugInfo) { @@ -562,13 +567,13 @@ export class RealTimeVisualizer3D implements Visualizer { // labelRenderer.domElement.style.top = '0px'; // element.appendChild(labelRenderer.domElement); - const orbitControls = new OrbitControls(camera, this.renderer.domElement); + const orbitControls = new OrbitControls(camera, renderer.domElement); orbitControls.listenToKeyEvents(window); orbitControls.update(); const axesHelper = new THREE.AxesHelper(width); this.scene.add(axesHelper); - const viewHelper = new ViewHelper(camera, this.renderer.domElement); + const viewHelper = new ViewHelper(camera, renderer.domElement); // var m: Map = new Map(); let arr: THREE.LineSegments[] = []; @@ -582,7 +587,7 @@ export class RealTimeVisualizer3D implements Visualizer { scale, ), ); - u.currState.bodies.forEach((b) => { + u.currState.bodies.forEach((b, i) => { const sph = new THREE.SphereGeometry( clipMinMax(Math.log2(b.mass) - 70, 10, 40), 8, @@ -592,8 +597,9 @@ export class RealTimeVisualizer3D implements Visualizer { const line = new THREE.LineSegments( curr, new THREE.LineBasicMaterial({ - // @ts-ignore - color: new THREE.Color(u.color), + color: new THREE.Color( + typeof u.color === 'string' ? u.color : u.color[i], + ), }), ); this.scene!.add(line); @@ -632,9 +638,9 @@ export class RealTimeVisualizer3D implements Visualizer { || this.simulation.controls.paused ) { this.animationId = requestAnimationFrame(paint); - this.renderer!.clear(); - this.renderer!.render(this.scene!, camera); - viewHelper.render(this.renderer!); + renderer.clear(); + renderer.render(this.scene!, camera); + viewHelper.render(renderer); // labelRenderer.render(scene, camera); orbitControls.update(); return; @@ -643,9 +649,9 @@ export class RealTimeVisualizer3D implements Visualizer { if (timePerFrame > 0 && timestampMs - lastPaint < timePerFrame) { this.animationId = requestAnimationFrame(paint); - this.renderer!.clear(); - this.renderer!.render(this.scene!, camera); - viewHelper.render(this.renderer!); + renderer.clear(); + renderer.render(this.scene!, camera); + viewHelper.render(renderer); // labelRenderer.render(scene, camera); orbitControls.update(); return; @@ -676,13 +682,31 @@ export class RealTimeVisualizer3D implements Visualizer { } }); this.animationId = requestAnimationFrame(paint); - this.renderer!.clear(); - this.renderer!.render(this.scene!, camera); - viewHelper.render(this.renderer!); + renderer.clear(); + renderer.render(this.scene!, camera); + viewHelper.render(renderer); // labelRenderer.render(scene, camera); orbitControls.update(); }; + /** + * Clear the objects and renderer. + */ + this.clear = () => { + renderer.clear(); + renderer.dispose(); + arr.forEach((a) => { + if (Array.isArray(a.material)) { + a.material.forEach((m) => { + m.dispose(); + }); + } else { + a.material.dispose(); + } + a.geometry.dispose(); + }); + }; + this.animationId = requestAnimationFrame(paint); } @@ -694,11 +718,13 @@ export class RealTimeVisualizer3D implements Visualizer { return; } cancelAnimationFrame(this.animationId); - this.renderer?.clear(); - this.renderer?.dispose(); this.scene?.clear(); this.scene = undefined; - this.renderer = null; + this.clear(); + /** + * Replace back the empty function. + */ + this.clear = () => { }; this.universeTrails.forEach((ut) => { ut.popAllTrails(); }); @@ -961,9 +987,12 @@ export class RecordingVisualizer implements Visualizer { return; } cancelAnimationFrame(this.animationId); - Plotly.purge(this.divId); this.divId = ''; this.universeTrails = []; + try { + Plotly.purge(this.divId); + } catch (_) { + } } } @@ -973,6 +1002,10 @@ export class RecordingVisualizer implements Visualizer { */ export class RecordingVisualizer3D implements Visualizer { animationId: number | null = null; + /** + * Clear the visualization. + */ + clear: () => void = () => { }; simulation: Simulation; scene?: THREE.Scene; universeTrails: ThreeUniverseTrail[] = []; @@ -1118,7 +1151,7 @@ export class RecordingVisualizer3D implements Visualizer { scale, ), ); - u.currState.bodies.forEach((b) => { + u.currState.bodies.forEach((b, i) => { const sph = new THREE.SphereGeometry( clipMinMax(Math.log2(b.mass) - 70, 10, 40), 8, @@ -1128,8 +1161,9 @@ export class RecordingVisualizer3D implements Visualizer { const line = new THREE.LineSegments( curr, new THREE.LineBasicMaterial({ - // @ts-ignore - color: new THREE.Color(u.color), + color: new THREE.Color( + typeof u.color === 'string' ? u.color : u.color[i], + ), }), ); this.scene!.add(line); @@ -1220,6 +1254,23 @@ export class RecordingVisualizer3D implements Visualizer { orbitControls.update(); }; + /** + * Clear the objects and renderer. + */ + this.clear = () => { + renderer.clear(); + renderer.dispose(); + arr.forEach((a) => { + if (Array.isArray(a.material)) { + a.material.forEach((m) => { + m.dispose(); + }); + } else { + a.material.dispose(); + } + a.geometry.dispose(); + }); + }; this.animationId = requestAnimationFrame(paint); } @@ -1233,6 +1284,11 @@ export class RecordingVisualizer3D implements Visualizer { cancelAnimationFrame(this.animationId); this.scene?.clear(); this.scene = undefined; + this.clear(); + /** + * Replace back the empty function. + */ + this.clear = () => { }; this.universeTrails.forEach((ut) => { ut.popAllTrails(); });