, State> {
13 | state = {};
14 | render() {
15 | const className = styles.InputList + " " + styles.Expression;
16 | const rowGap = this.props.rowGap ?? 0;
17 | return (
18 |
19 | {this.props.children}
20 |
21 | );
22 | }
23 | }
24 | export default observer(InputList);
25 |
--------------------------------------------------------------------------------
/src-tauri/capabilities/main.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./schemas/desktop-schema.json",
3 | "identifier": "main-capability",
4 | "description": "Capability for the main window",
5 | "windows": ["main"],
6 | "permissions": [
7 | "core:app:allow-version",
8 | "core:event:allow-emit",
9 | "core:event:allow-listen",
10 | "core:event:allow-unlisten",
11 | "core:path:allow-basename",
12 | "core:path:allow-dirname",
13 | "core:path:allow-join",
14 | "core:webview:allow-internal-toggle-devtools",
15 | "core:window:allow-close",
16 | "core:window:allow-destroy",
17 | "core:window:allow-set-title",
18 | "dialog:allow-ask",
19 | "dialog:allow-confirm",
20 | "dialog:allow-open",
21 | "dialog:allow-save"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/field/svg/FieldAxisLines.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from "mobx-react";
2 |
3 | const DRAW_BOUND = 100;
4 | const GRID_STROKE = 0.01;
5 |
6 | function FieldGrid() {
7 | return (
8 | <>
9 |
18 |
27 | >
28 | );
29 | }
30 | export default observer(FieldGrid);
31 |
--------------------------------------------------------------------------------
/src-core/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "choreo-core"
3 | version = "2026.0.0-beta.1"
4 | edition = "2024"
5 | homepage = "https://github.com/SleipnirGroup/Choreo"
6 | repository = "https://github.com/SleipnirGroup/Choreo.git"
7 | authors = ["Sleipnir Group"]
8 | license = "BSD-3-Clause"
9 |
10 | [dependencies]
11 | dashmap = "6.1.0"
12 | fastrand = "2.3.0"
13 | futures-util = "0.3.31"
14 | ipc-channel = { version = "0.20.2", features = ["async"] }
15 | serde = { version = "1.0", features = ["derive"] }
16 | serde_json = "1.0"
17 | tempfile = "3.23.0"
18 | thiserror = "2.0.17"
19 | tokio = { version = "1", features = ["process", "macros", "rt", "fs", "sync", "io-util"] }
20 | tracing = "0.1.43"
21 | trajoptlib = { path = "../trajoptlib" }
22 | zip = { version = "6.0.0", default-features = false }
23 |
--------------------------------------------------------------------------------
/src/assets/KeepInLane.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { SvgIcon as MuiSvgIcon, SvgIconProps, styled } from "@mui/material";
3 | const SvgIcon = styled(MuiSvgIcon, {
4 | name: "KeepInLaneIcon",
5 | shouldForwardProp: (prop) => prop !== "fill"
6 | })(() => ({
7 | fill: "currentColor",
8 | stroke: "none"
9 | }));
10 |
11 | const KeepInLane: React.FunctionComponent = (props) => {
12 | return (
13 |
19 |
20 |
21 | );
22 | };
23 | export default KeepInLane;
24 |
--------------------------------------------------------------------------------
/src/util/BuildInfo.ts:
--------------------------------------------------------------------------------
1 | import { invoke } from "@tauri-apps/api/core";
2 |
3 | export interface BuildInfo {
4 | ciPlatform: string | null;
5 | pkgName: string;
6 | pkgVersion: string;
7 | pkgVersionMajor: string;
8 | pkgVersionMinor: string;
9 | pkgVersionPatch: string;
10 | pkgVersionPre: string;
11 | target: string;
12 | host: string;
13 | profile: string;
14 | rustc: string;
15 | optLevel: string;
16 | debug: boolean;
17 | features: string[];
18 | rustcVersion: string;
19 | arch: string;
20 | endian: string;
21 | toolchain_env: string;
22 | osFamily: string;
23 | os: string;
24 | buildTime: string;
25 | gitHash: string | null;
26 | gitBranch: string | null;
27 | }
28 |
29 | export function getBuildInfo(): Promise {
30 | return invoke("build_info");
31 | }
32 |
--------------------------------------------------------------------------------
/trajoptlib/test/src/util/generate_linear_initial_guess_test.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) TrajoptLib contributors
2 |
3 | #include
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | TEST_CASE("generate_linear_initial_guess - Linear initial guess",
10 | "[TrajoptUtil]") {
11 | std::vector> initial_guess_points{
12 | {{1, 0, 0}}, {{2, 0, 0}, {3, 0, 0}}, {{6, 0, 0}}};
13 | std::vector control_interval_counts{2, 3};
14 | std::vector expected_x{1, 2, 3, 4, 5, 6};
15 | auto result = trajopt::generate_linear_initial_guess(
16 | initial_guess_points, control_interval_counts);
17 | CHECK(expected_x == result.x);
18 | }
19 |
--------------------------------------------------------------------------------
/src/util/UnitConversions.ts:
--------------------------------------------------------------------------------
1 | export const MassUnit = function (imperial: boolean) {
2 | return imperial ? "lb" : "kg";
3 | };
4 | export const KG_TO_LBS = 2.02462;
5 | export const KgToLbs = (mass: number) => mass * KG_TO_LBS;
6 | export const LbsToKg = (mass: number) => mass / KG_TO_LBS;
7 |
8 | export const MetersOrInches = function (imperial: boolean) {
9 | return imperial ? "in" : "m";
10 | };
11 | export const M_TO_IN = 39.3701;
12 | export const MToIn = (length: number) => length * M_TO_IN;
13 | export const InToM = (length: number) => length / M_TO_IN;
14 |
15 | export const MetersOrFeet = function (imperial: boolean) {
16 | return imperial ? "ft" : "m";
17 | };
18 | export const M_TO_FT = M_TO_IN / 12;
19 | export const MToFt = (length: number) => length * M_TO_FT;
20 | export const FtToM = (length: number) => length / M_TO_FT;
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | !.vscode/settings.json
19 | .idea
20 | .DS_Store
21 | *.suo
22 | *.ntvs*
23 | *.njsproj
24 | *.sln
25 | *.sw?
26 |
27 | # Choreo
28 | src-tauri/gen
29 | src-tauri/target
30 | target
31 |
32 | # TrajoptLib
33 | trajoptlib/build*
34 | trajoptlib/target
35 | trajoptlib/Cargo.lock
36 |
37 | # Python
38 | *.egg-info/
39 | .eggs/
40 | .pytest_cache/
41 | __pycache__/
42 | dist/
43 |
44 | # clangd
45 | .cache
46 | compile_commands.json
47 |
48 | # mkdocs
49 | site/
50 |
51 | # cmake user presets
52 | CMakeUserPresets.json
53 |
54 | build/
55 | cli/
56 | test-tmp*/
57 |
--------------------------------------------------------------------------------
/choreolib/py/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "sleipnirgroup-choreolib"
3 | description = "Robot-side library for parsing and following Choreo paths"
4 | dynamic = ["version"]
5 | readme = "README.md"
6 | license = "BSD-3-Clause"
7 | license-files = ["LICENSE.txt"]
8 | requires-python = ">=3.9"
9 | dependencies = ["numpy", "scipy", "wpilib"]
10 |
11 | [[project.authors]]
12 | name = "Choreo Development Team"
13 |
14 | [project.urls]
15 | Documentation = "https://choreo.autos/"
16 |
17 | [build-system]
18 | requires = [
19 | "setuptools>=61.0",
20 | "setuptools-git-versioning",
21 | ]
22 | build-backend = "setuptools.build_meta"
23 |
24 | [tool.setuptools-git-versioning]
25 | enabled = true
26 | version_callback = "_choreo_version:get_version"
27 |
28 | [tool.pytest.ini_options]
29 | minversion = "6.0"
30 | testpaths = ["choreo/test"]
31 |
--------------------------------------------------------------------------------
/src/components/input/InputList.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from "mobx-react";
2 | import React, { Component, PropsWithChildren } from "react";
3 | import styles from "./InputList.module.css";
4 |
5 | type Props = {
6 | noCheckbox?: boolean;
7 | rowGap?: number;
8 | style?: React.CSSProperties;
9 | };
10 |
11 | type State = object;
12 |
13 | class InputList extends Component, State> {
14 | state = {};
15 | render() {
16 | const className =
17 | styles.InputList +
18 | " " +
19 | ((this.props.noCheckbox ?? false) ? styles.NoCheckbox : "");
20 | const rowGap = this.props.rowGap ?? 0;
21 | return (
22 |
23 | {this.props.children}
24 |
25 | );
26 | }
27 | }
28 | export default observer(InputList);
29 |
--------------------------------------------------------------------------------
/src/components/field/svg/FieldPathLines.tsx:
--------------------------------------------------------------------------------
1 | import { Component } from "react";
2 | import { doc } from "../../../document/DocumentManager";
3 |
4 | import { observer } from "mobx-react";
5 |
6 | type Props = object;
7 |
8 | type State = object;
9 |
10 | class FieldPathLines extends Component {
11 | state = {};
12 |
13 | render() {
14 | let pathString = "";
15 | doc.pathlist.activePath.params.waypoints.forEach((point, _index) => {
16 | pathString += `${point.x.value}, ${point.y.value} `;
17 | });
18 | return (
19 | <>
20 |
27 | >
28 | );
29 | }
30 | }
31 | export default observer(FieldPathLines);
32 |
--------------------------------------------------------------------------------
/choreolib/test_deploy/test.chor:
--------------------------------------------------------------------------------
1 | {
2 | "name":"idk",
3 | "version":"v2025.0.0",
4 | "type":"Swerve",
5 | "variables":{
6 | "expressions":{},
7 | "poses":{}
8 | },
9 | "config":{
10 | "modules":[
11 | {"x":["11 in",0.2794], "y":["11 in",0.2794]},
12 | {"x":["-11 in",-0.2794], "y":["11 in",0.2794]},
13 | {"x":["-11 in",-0.2794], "y":["-11 in",-0.2794]},
14 | {"x":["11 in",0.2794], "y":["-11 in",-0.2794]}],
15 | "mass":["150 lbs",68.0388555],
16 | "inertia":["6 kg m ^ 2",6.0],
17 | "gearing":["6.5",6.5],
18 | "radius":["2 in",0.0508],
19 | "vmax":["6000 RPM",628.3185307179587],
20 | "tmax":["1.2 N * m",1.2],
21 | "bumper":{
22 | "front":["16 in",0.4064],
23 | "left":["16 in",0.4064],
24 | "back":["16 in",0.4064],
25 | "right":["16 in",0.4064]
26 | },
27 | "differentialTrackWidth":["24 in",0.6096],
28 | },
29 | "generationFeatures":[]
30 | }
31 |
--------------------------------------------------------------------------------
/docs/contributing/building-choreolib.md:
--------------------------------------------------------------------------------
1 | # Building ChoreoLib
2 |
3 | Maven artifacts for ChoreoLib can be built using `./gradlew publish` or `./gradlew publishToMavenLocal` for local library access.
4 |
5 | The built library will be located in the respective operating system's m2 folder. By default, Maven local repository is defaulted to the `${user.home}/.m2/repository` folder:
6 |
7 | === "Windows"
8 |
9 | ```
10 | %HOMEPATH%\.m2\repository
11 | ```
12 |
13 | === "macOS/UNIX"
14 |
15 | ```
16 | ~/.m2/repository
17 | ```
18 |
19 | To use your build, update `vendordeps/ChoreoLib.json` to point to the local repository and version.
20 |
21 | !!! danger
22 | If you attempt to work with this project in VSCode with WPILib plugins, it will ask you if you want to import the project. Click no. This will change the project into a robot code project and break everything.
23 |
--------------------------------------------------------------------------------
/docs/installation.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | Start by downloading Choreo from **[Releases](https://github.com/SleipnirGroup/Choreo/releases)**
4 |
5 | ## Supported Systems
6 |
7 | | Operating System | Supported? |
8 | | ------------------------------------ | ---------- |
9 | | Windows x86\_64 | ✅ |
10 | | Windows aarch64 | ✅ |
11 | | macOS x86\_64 | ✅ |
12 | | macOS arm64 | ✅ |
13 | | Linux x86\_64 | ✅ |
14 | | Linux aarch64 | ❌ |
15 |
16 | Checkout [Building Choreo](./contributing/building-choreo.md) for instruction on how to build Choreo as well as the tech stack.
17 |
18 | Once installed, head to [Robot Config](./document-settings/robot-configuration.md) to configure Choreo for your robot.
19 |
--------------------------------------------------------------------------------
/src/assets/InitialGuessPoint.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { SvgIcon as MuiSvgIcon, SvgIconProps, styled } from "@mui/material";
3 | const SvgIcon = styled(MuiSvgIcon, {
4 | name: "InitialGuessPoint",
5 | shouldForwardProp: (prop) => prop !== "fill"
6 | })(() => ({
7 | fill: "none",
8 | stroke: "currentColor",
9 | strokeLinecap: "round",
10 | strokeLinejoin: "round",
11 | strokeWidth: "2.25px"
12 | }));
13 |
14 | const Waypoint: React.FunctionComponent = (props) => {
15 | return (
16 |
22 |
29 |
30 | );
31 | };
32 | export default Waypoint;
33 |
--------------------------------------------------------------------------------
/src-core/src/generation/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod generate;
2 | pub mod heading;
3 | pub mod intervals;
4 | pub mod remote;
5 |
6 | /// A port of `WPILib`'s MathUtil.inputModulus
7 | #[must_use]
8 | pub fn input_modulus(input: f64, maximum_input: f64, minimum_input: f64) -> f64 {
9 | let mut val = input;
10 | let modulus = maximum_input - minimum_input;
11 |
12 | // Wrap input if it's above the maximum input
13 | let num_max = ((val - minimum_input) / modulus).trunc();
14 | val -= num_max * modulus;
15 |
16 | // Wrap input if it's below the minimum input
17 | let num_min = ((val - maximum_input) / modulus).trunc();
18 | val -= num_min * modulus;
19 |
20 | val
21 | }
22 |
23 | /// A port of `WPILib`'s MathUtil.angleModulus
24 | #[must_use]
25 | pub fn angle_modulus(input: f64) -> f64 {
26 | use std::f64::consts::PI;
27 | input_modulus(input, PI, -PI)
28 | }
29 | pub mod transformers;
30 |
--------------------------------------------------------------------------------
/src/assets/Waypoint.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { SvgIcon as MuiSvgIcon, SvgIconProps, styled } from "@mui/material";
3 | const SvgIcon = styled(MuiSvgIcon, {
4 | name: "MopeimIcon",
5 | shouldForwardProp: (prop) => prop !== "fill"
6 | })(() => ({
7 | fill: "none",
8 | stroke: "currentColor",
9 | strokeLinecap: "round",
10 | strokeLinejoin: "round",
11 | strokeWidth: "2.25px"
12 | }));
13 |
14 | const Waypoint: React.FunctionComponent = (props) => {
15 | return (
16 |
22 |
23 |
24 |
25 |
26 | );
27 | };
28 | export default Waypoint;
29 |
--------------------------------------------------------------------------------
/choreolib/src/main/native/cpp/choreo/trajectory/EventMarker.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) Choreo contributors
2 |
3 | #include "choreo/trajectory/EventMarker.h"
4 |
5 | #include
6 |
7 | #include
8 |
9 | void choreo::to_json(wpi::json& json, const EventMarker& event) {
10 | json = wpi::json{{"data", wpi::json{{"t", event.timestamp.value()}}},
11 | {"event", wpi::json{{"name", event.event}}}};
12 | }
13 |
14 | void choreo::from_json(const wpi::json& json, EventMarker& event) {
15 | auto targetTimestamp = json.at("from").at("targetTimestamp");
16 | if (!targetTimestamp.is_number()) {
17 | event.timestamp = units::second_t{-1};
18 | event.event = "";
19 | } else {
20 | event.timestamp =
21 | units::second_t{json.at("from").at("offset").at("val").get() +
22 | targetTimestamp.get()};
23 | event.event = json.at("name").get();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/trajoptlib/docs/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
15 |
16 |
22 |