mechanicsdsl-unity 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. mechanicsdsl_unity-0.1.0/.github/workflows/publish-pypi.yml +85 -0
  2. mechanicsdsl_unity-0.1.0/.gitignore +47 -0
  3. mechanicsdsl_unity-0.1.0/CHANGELOG.md +20 -0
  4. mechanicsdsl_unity-0.1.0/CITATION.cff +30 -0
  5. mechanicsdsl_unity-0.1.0/CONTRIBUTING.md +47 -0
  6. mechanicsdsl_unity-0.1.0/Editor/DoublePendulumComponentEditor.cs +57 -0
  7. mechanicsdsl_unity-0.1.0/Editor/PendulumComponentEditor.cs +121 -0
  8. mechanicsdsl_unity-0.1.0/Editor/com.mechanicsdsl.unity.editor.asmdef +18 -0
  9. mechanicsdsl_unity-0.1.0/LICENSE +21 -0
  10. mechanicsdsl_unity-0.1.0/PKG-INFO +158 -0
  11. mechanicsdsl_unity-0.1.0/README.md +133 -0
  12. mechanicsdsl_unity-0.1.0/Runtime/Components/CoupledPendulumsComponent.cs +138 -0
  13. mechanicsdsl_unity-0.1.0/Runtime/Components/DoublePendulumComponent.cs +139 -0
  14. mechanicsdsl_unity-0.1.0/Runtime/Components/PendulumComponent.cs +250 -0
  15. mechanicsdsl_unity-0.1.0/Runtime/Utilites/ConservationMonitor.cs +105 -0
  16. mechanicsdsl_unity-0.1.0/Runtime/Utilites/MechanicsDSLMath.cs +69 -0
  17. mechanicsdsl_unity-0.1.0/Runtime/Utilites/PhaseSpaceTrail.cs +110 -0
  18. mechanicsdsl_unity-0.1.0/Runtime/com.mechanicsdsl.unity.runtime.asmdef +14 -0
  19. mechanicsdsl_unity-0.1.0/Samples~/DoublePendulum/README.md +38 -0
  20. mechanicsdsl_unity-0.1.0/Samples~/SimplePendulum/README.md +44 -0
  21. mechanicsdsl_unity-0.1.0/Tests/Runtime/MechanicsDSL.Tests.Runtime.asmdef +19 -0
  22. mechanicsdsl_unity-0.1.0/Tests/Runtime/TestDoublePendulumEOM.cs +71 -0
  23. mechanicsdsl_unity-0.1.0/Tests/Runtime/TestPendulumEOM.cs +87 -0
  24. mechanicsdsl_unity-0.1.0/docs/adding_systems.md +83 -0
  25. mechanicsdsl_unity-0.1.0/docs/components_reference.md +71 -0
  26. mechanicsdsl_unity-0.1.0/docs/getting_started.md +68 -0
  27. mechanicsdsl_unity-0.1.0/package.json +38 -0
  28. mechanicsdsl_unity-0.1.0/pyproject.toml +47 -0
  29. mechanicsdsl_unity-0.1.0/src/mechanicsdsl_unity/__init__.py +19 -0
  30. mechanicsdsl_unity-0.1.0/src/mechanicsdsl_unity/_components.py +89 -0
  31. mechanicsdsl_unity-0.1.0/src/mechanicsdsl_unity/_version.py +1 -0
@@ -0,0 +1,85 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*.*.*'
7
+ workflow_dispatch:
8
+ inputs:
9
+ test_pypi:
10
+ description: 'Publish to PyPI'
11
+ type: boolean
12
+ default: false
13
+
14
+ jobs:
15
+ build:
16
+ name: Build distribution
17
+ runs-on: ubuntu-latest
18
+
19
+ steps:
20
+ - name: Checkout
21
+ uses: actions/checkout@v4
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: '3.11'
27
+
28
+ - name: Install build tools
29
+ run: pip install hatch build twine
30
+
31
+ - name: Build package
32
+ run: python -m build
33
+
34
+ - name: Check distribution
35
+ run: twine check dist/*
36
+
37
+ - name: Upload distribution artifacts
38
+ uses: actions/upload-artifact@v4
39
+ with:
40
+ name: dist
41
+ path: dist/
42
+
43
+ publish-pypi:
44
+ name: Publish to PyPI
45
+ needs: build
46
+ runs-on: ubuntu-latest
47
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && !inputs.test_pypi
48
+ environment:
49
+ name: pypi
50
+ url: https://pypi.org/p/${{ github.event.repository.name }}
51
+ permissions:
52
+ id-token: write # Required for trusted publishing
53
+
54
+ steps:
55
+ - name: Download artifacts
56
+ uses: actions/download-artifact@v4
57
+ with:
58
+ name: dist
59
+ path: dist/
60
+
61
+ - name: Publish to PyPI
62
+ uses: pypa/gh-action-pypi-publish@release/v1
63
+
64
+ publish-testpypi:
65
+ name: Publish to TestPyPI
66
+ needs: build
67
+ runs-on: ubuntu-latest
68
+ if: inputs.test_pypi
69
+ environment:
70
+ name: testpypi
71
+ url: https://test.pypi.org/p/${{ github.event.repository.name }}
72
+ permissions:
73
+ id-token: write
74
+
75
+ steps:
76
+ - name: Download artifacts
77
+ uses: actions/download-artifact@v4
78
+ with:
79
+ name: dist
80
+ path: dist/
81
+
82
+ - name: Publish to TestPyPI
83
+ uses: pypa/gh-action-pypi-publish@release/v1
84
+ with:
85
+ repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,47 @@
1
+ # Unity
2
+ [Ll]ibrary/
3
+ [Tt]emp/
4
+ [Oo]bj/
5
+ [Bb]uild/
6
+ [Bb]uilds/
7
+ [Ll]ogs/
8
+ [Mm]emoryCaptures/
9
+ [Uu]serSettings/
10
+ *.pidb.meta
11
+ *.pdb.meta
12
+ *.mdb.meta
13
+ sysinfo.txt
14
+ *.apk
15
+ *.aab
16
+ *.unitypackage
17
+ *.app
18
+ crashlytics-build.properties
19
+ .DS_Store
20
+ *.swp
21
+ *.swo
22
+
23
+ # Unreal Engine
24
+ Binaries/
25
+ DerivedDataCache/
26
+ Intermediate/
27
+ Saved/
28
+ .vs/
29
+ *.VC.db
30
+ *.opensdf
31
+ *.opendb
32
+ *.sdf
33
+ *.sln
34
+ *.suo
35
+ *.xcworkspace
36
+ *.xcodeproj
37
+ *.pdb
38
+
39
+ # Generated code (tracked separately)
40
+ Generated/
41
+
42
+ # Python (for code generation tooling)
43
+ __pycache__/
44
+ *.py[cod]
45
+ .venv/
46
+ venv/
47
+ .env
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to mechanicsdsl-unity are documented here.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com).
5
+
6
+ ## [Unreleased]
7
+
8
+ ### Added
9
+ - `PendulumComponent` — simple pendulum MonoBehaviour with RK4, Noether monitor, Gizmos
10
+ - `DoublePendulumComponent` — double pendulum MonoBehaviour with chaos detection
11
+ - `MechanicsDSLMath` — shared utilities: generic RK4, symplectic Euler, angle wrapping
12
+ - `ConservationMonitor` — on-screen HUD for Noether conservation law monitoring
13
+ - `PendulumComponentEditor` — custom Inspector with live state readout and reset button
14
+ - UPM `package.json` manifest with sample declarations
15
+ - Runtime tests: `TestPendulumEOM`
16
+ - Sample documentation: SimplePendulum, DoublePendulum
17
+
18
+ ## [0.1.0] — 2026-03-13
19
+
20
+ - Initial repository scaffold
@@ -0,0 +1,30 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software in your project, please cite:"
3
+ type: software
4
+ title: "MechanicsDSL Unity"
5
+ abstract: >
6
+ Unity and Unreal Engine plugin packages for MechanicsDSL.
7
+ Provides physically accurate simulation components compiled from
8
+ DSL notation, targeting game development, digital twins,
9
+ interactive science exhibits, and educational physics visualisations.
10
+ authors:
11
+ - family-names: Parsons
12
+ given-names: Noah
13
+ orcid: "https://orcid.org/0009-0000-7224-6040"
14
+ affiliation: "American Forge Institute & Eastern Wyoming College"
15
+ repository-code: "https://github.com/MechanicsDSL/mechanicsdsl-unity"
16
+ license: MIT
17
+ date-released: "2026-03-13"
18
+ keywords:
19
+ - Unity
20
+ - Unreal Engine
21
+ - physics simulation
22
+ - Lagrangian mechanics
23
+ - game engine
24
+ references:
25
+ - type: software
26
+ title: "MechanicsDSL"
27
+ doi: "10.5281/zenodo.17771040"
28
+ authors:
29
+ - family-names: Parsons
30
+ given-names: Noah
@@ -0,0 +1,47 @@
1
+ # Contributing to mechanicsdsl-unity
2
+
3
+ ## Contribution Types
4
+
5
+ ### New Physics Components
6
+
7
+ Generate a new component from DSL notation:
8
+
9
+ ```bash
10
+ mechanicsdsl generate my_system.msl --target unity --out Runtime/Components/
11
+ mechanicsdsl generate my_system.msl --target unity_editor --out Editor/
12
+ ```
13
+
14
+ Include:
15
+ - The originating `.msl` specification as a header comment
16
+ - An `AddComponentMenu` attribute for discoverability
17
+ - A custom Inspector Editor script with live state readout
18
+ - Runtime tests in `Tests/Runtime/`
19
+ - Entry in `CHANGELOG.md`
20
+
21
+ ### Sample Scenes
22
+
23
+ Well-documented sample scenes demonstrating MechanicsDSL components are especially valuable for new users. Add samples to `Samples~/YourSampleName/` and declare them in `package.json`.
24
+
25
+ ### Bug Reports
26
+
27
+ Include:
28
+ - Unity version
29
+ - How to reproduce (minimal scene setup)
30
+ - Expected vs actual behaviour
31
+ - Whether the issue is in the EOM, integrator, or Unity integration layer
32
+
33
+ ## Testing
34
+
35
+ Run tests via **Window → General → Test Runner → PlayMode → Run All**.
36
+
37
+ All physics components must have:
38
+ - Energy conservation test (5+ seconds simulation)
39
+ - Reset/initial-conditions test
40
+ - Equilibrium stability test
41
+
42
+ ## Code Style
43
+
44
+ - Use `[AddComponentMenu("MechanicsDSL/...")]` for all MonoBehaviours
45
+ - All MechanicsDSL-generated code must be clearly labelled with the originating DSL spec
46
+ - Inspector properties should have `[Tooltip]` attributes
47
+ - Use `[Range]` attributes for physical parameters to prevent unphysical values
@@ -0,0 +1,57 @@
1
+ #if UNITY_EDITOR
2
+ using UnityEditor;
3
+ using UnityEngine;
4
+ using MechanicsDSL.Classical;
5
+
6
+ namespace MechanicsDSL.Classical.Editor
7
+ {
8
+ [CustomEditor(typeof(DoublePendulumComponent))]
9
+ public class DoublePendulumComponentEditor : UnityEditor.Editor
10
+ {
11
+ private bool _showState = true;
12
+
13
+ public override void OnInspectorGUI()
14
+ {
15
+ var comp = (DoublePendulumComponent)target;
16
+ serializedObject.Update();
17
+
18
+ EditorGUILayout.Space(4);
19
+ EditorGUILayout.LabelField("MechanicsDSL — Double Pendulum", EditorStyles.boldLabel);
20
+ EditorGUILayout.LabelField("L = ½ml²(2θ̇₁²+θ̇₂²+2θ̇₁θ̇₂cos Δθ)+mgl(2cosθ₁+cosθ₂)", EditorStyles.miniLabel);
21
+ EditorGUILayout.Space(6);
22
+
23
+ DrawPropertiesExcluding(serializedObject, "m_script");
24
+
25
+ if (Application.isPlaying)
26
+ {
27
+ EditorGUILayout.Space(8);
28
+ _showState = EditorGUILayout.Foldout(_showState, "Live State", true, EditorStyles.foldoutHeader);
29
+ if (_showState)
30
+ {
31
+ EditorGUI.indentLevel++;
32
+ EditorGUILayout.LabelField("θ₁ (rad)", $"{comp.Theta1:F6}");
33
+ EditorGUILayout.LabelField("θ₂ (rad)", $"{comp.Theta2:F6}");
34
+ EditorGUILayout.LabelField("ω₁ (rad/s)", $"{comp.Omega1:F6}");
35
+ EditorGUILayout.LabelField("ω₂ (rad/s)", $"{comp.Omega2:F6}");
36
+ EditorGUILayout.LabelField("t (s)", $"{comp.SimTime:F3}");
37
+ EditorGUILayout.LabelField("Energy (J)", $"{comp.Energy:F8}");
38
+
39
+ Color prev = GUI.color;
40
+ GUI.color = comp.EnergyDrift > comp.driftTolerance
41
+ ? new Color(1f,0.4f,0.4f) : new Color(0.4f,1f,0.6f);
42
+ EditorGUILayout.LabelField("|ΔE/E₀|",
43
+ comp.EnergyDrift < 1e-10f ? "< 1e-10 ✓" : $"{comp.EnergyDrift:E3}");
44
+ GUI.color = prev;
45
+
46
+ EditorGUI.indentLevel--;
47
+ EditorGUILayout.Space(4);
48
+ if (GUILayout.Button("Reset")) comp.Reset();
49
+ }
50
+ Repaint();
51
+ }
52
+
53
+ serializedObject.ApplyModifiedProperties();
54
+ }
55
+ }
56
+ }
57
+ #endif
@@ -0,0 +1,121 @@
1
+ #if UNITY_EDITOR
2
+ using UnityEditor;
3
+ using UnityEngine;
4
+
5
+ namespace MechanicsDSL.Classical.Editor
6
+ {
7
+ /// <summary>
8
+ /// Custom Inspector for PendulumComponent.
9
+ /// Adds a live state readout, reset button, and energy drift gauge
10
+ /// directly in the Inspector during Play Mode.
11
+ /// </summary>
12
+ [CustomEditor(typeof(PendulumComponent))]
13
+ public class PendulumComponentEditor : UnityEditor.Editor
14
+ {
15
+ private bool _showState = true;
16
+ private bool _showPhysics = true;
17
+ private bool _showEvents = false;
18
+
19
+ public override void OnInspectorGUI()
20
+ {
21
+ var comp = (PendulumComponent)target;
22
+ serializedObject.Update();
23
+
24
+ // ------------------------------------------------------------------
25
+ // Header
26
+ // ------------------------------------------------------------------
27
+ EditorGUILayout.Space(4);
28
+ EditorGUILayout.LabelField("MechanicsDSL — Simple Pendulum",
29
+ EditorStyles.boldLabel);
30
+ EditorGUILayout.LabelField(
31
+ "Lagrangian: L = ½ml²ω² − mgl(1−cosθ)",
32
+ EditorStyles.miniLabel);
33
+ EditorGUILayout.Space(6);
34
+
35
+ // ------------------------------------------------------------------
36
+ // Physical parameters
37
+ // ------------------------------------------------------------------
38
+ _showPhysics = EditorGUILayout.Foldout(_showPhysics,
39
+ "Physical Parameters", true, EditorStyles.foldoutHeader);
40
+ if (_showPhysics)
41
+ {
42
+ EditorGUI.indentLevel++;
43
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("mass"));
44
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("length"));
45
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("gravity"));
46
+ EditorGUILayout.Space(4);
47
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("theta0"));
48
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("omega0"));
49
+ EditorGUI.indentLevel--;
50
+ }
51
+
52
+ EditorGUILayout.Space(4);
53
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("dt"));
54
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("driftTolerance"));
55
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("bobTransform"));
56
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("drawGizmos"));
57
+
58
+ // ------------------------------------------------------------------
59
+ // Live state readout (Play Mode only)
60
+ // ------------------------------------------------------------------
61
+ if (Application.isPlaying)
62
+ {
63
+ EditorGUILayout.Space(8);
64
+ _showState = EditorGUILayout.Foldout(_showState,
65
+ "Live State", true, EditorStyles.foldoutHeader);
66
+
67
+ if (_showState)
68
+ {
69
+ EditorGUI.indentLevel++;
70
+
71
+ EditorGUILayout.LabelField("θ (rad)",
72
+ $"{comp.Theta:F6}");
73
+ EditorGUILayout.LabelField("ω (rad/s)",
74
+ $"{comp.Omega:F6}");
75
+ EditorGUILayout.LabelField("t (s)",
76
+ $"{comp.SimTime:F3}");
77
+ EditorGUILayout.LabelField("Energy (J)",
78
+ $"{comp.Energy:F8}");
79
+
80
+ // Energy drift gauge
81
+ float drift = comp.EnergyDrift;
82
+ Color prevColor = GUI.color;
83
+ GUI.color = drift > comp.driftTolerance
84
+ ? new Color(1f, 0.4f, 0.4f)
85
+ : new Color(0.4f, 1f, 0.6f);
86
+ EditorGUILayout.LabelField("|ΔE/E₀|",
87
+ drift < 1e-10f ? "< 1e-10 ✓" : $"{drift:E3}");
88
+ GUI.color = prevColor;
89
+
90
+ EditorGUI.indentLevel--;
91
+
92
+ EditorGUILayout.Space(4);
93
+ if (GUILayout.Button("Reset to Initial Conditions"))
94
+ comp.ResetToInitialConditions();
95
+ }
96
+
97
+ // Repaint every frame during play to keep live readout updated
98
+ Repaint();
99
+ }
100
+
101
+ // ------------------------------------------------------------------
102
+ // Events (collapsed by default — keep Inspector clean)
103
+ // ------------------------------------------------------------------
104
+ EditorGUILayout.Space(4);
105
+ _showEvents = EditorGUILayout.Foldout(_showEvents,
106
+ "Events", true, EditorStyles.foldoutHeader);
107
+ if (_showEvents)
108
+ {
109
+ EditorGUI.indentLevel++;
110
+ EditorGUILayout.PropertyField(
111
+ serializedObject.FindProperty("OnStateUpdate"));
112
+ EditorGUILayout.PropertyField(
113
+ serializedObject.FindProperty("OnEnergyDrift"));
114
+ EditorGUI.indentLevel--;
115
+ }
116
+
117
+ serializedObject.ApplyModifiedProperties();
118
+ }
119
+ }
120
+ }
121
+ #endif
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "com.mechanicsdsl.unity.editor",
3
+ "rootNamespace": "MechanicsDSL.Classical.Editor",
4
+ "references": [
5
+ "com.mechanicsdsl.unity.runtime"
6
+ ],
7
+ "includePlatforms": [
8
+ "Editor"
9
+ ],
10
+ "excludePlatforms": [],
11
+ "allowUnsafeCode": false,
12
+ "overrideReferences": false,
13
+ "precompiledReferences": [],
14
+ "autoReferenced": true,
15
+ "defineConstraints": [],
16
+ "versionDefines": [],
17
+ "noEngineReferences": false
18
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MechanicsDSL
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.4
2
+ Name: mechanicsdsl-unity
3
+ Version: 0.1.0
4
+ Summary: Unity and Unreal Engine plugin packages for MechanicsDSL — physically accurate simulation components compiled from DSL notation
5
+ Project-URL: Homepage, https://github.com/MechanicsDSL/mechanicsdsl-unity
6
+ Project-URL: Repository, https://github.com/MechanicsDSL/mechanicsdsl-unity
7
+ Project-URL: DOI, https://doi.org/10.5281/zenodo.17771040
8
+ Author-email: Noah Parsons <nomapa223@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: Lagrangian mechanics,MechanicsDSL,digital-twin,game-engine,physics simulation,unity,unreal-engine
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Games/Entertainment
18
+ Classifier: Topic :: Scientific/Engineering :: Physics
19
+ Requires-Python: >=3.9
20
+ Requires-Dist: mechanicsdsl-core
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0; extra == 'dev'
23
+ Requires-Dist: ruff; extra == 'dev'
24
+ Description-Content-Type: text/markdown
25
+
26
+ <p align="center">
27
+ <img src="https://raw.githubusercontent.com/MechanicsDSL/mechanicsdsl/main/docs/images/logo.png" alt="MechanicsDSL Logo" width="360">
28
+ </p>
29
+
30
+ <h1 align="center">mechanicsdsl-unity</h1>
31
+
32
+ <p align="center">
33
+ <em>Physically accurate simulation components for Unity and Unreal Engine, compiled from DSL notation.</em>
34
+ </p>
35
+
36
+ <p align="center">
37
+ <img src="https://img.shields.io/badge/status-active-green" alt="Active">
38
+ <img src="https://img.shields.io/badge/Unity-2021.3%2B-black" alt="Unity">
39
+ <img src="https://img.shields.io/badge/components-3-blue" alt="3 Components">
40
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="MIT License"></a>
41
+ <a href="https://github.com/MechanicsDSL/mechanicsdsl"><img src="https://img.shields.io/badge/core-mechanicsdsl-blue" alt="Core Package"></a>
42
+ </p>
43
+
44
+ ---
45
+
46
+ ## Overview
47
+
48
+ `mechanicsdsl-unity` provides Unity MonoBehaviour components generated from MechanicsDSL DSL specifications. All components bypass PhysX with Lagrangian equations of motion, include Noether-based energy monitoring, and expose parameters in the Unity Inspector for real-time tuning.
49
+
50
+ ---
51
+
52
+ ## Components
53
+
54
+ ### Classical Mechanics
55
+
56
+ | Component | System | Conserved | Gizmos |
57
+ |-----------|--------|-----------|--------|
58
+ | `PendulumComponent` | Simple pendulum | Energy (Noether) | Pivot, rod, bob |
59
+ | `DoublePendulumComponent` | Double pendulum (chaotic) | Energy (Noether) | Full double pendulum |
60
+ | `CoupledPendulumsComponent` | Coupled pendulums | Energy (Noether) | Both pendulums + spring |
61
+
62
+ ### Utilities
63
+
64
+ | Component | Description |
65
+ |-----------|-------------|
66
+ | `ConservationMonitor` | On-screen HUD for energy drift across any MechanicsDSL component |
67
+ | `PhaseSpaceTrail` | Renders θ vs ω phase portrait as a LineRenderer trail |
68
+ | `MechanicsDSLMath` | Generic RK4, symplectic Euler, angle wrap, bob position helpers |
69
+
70
+ ### Editor
71
+
72
+ | Script | Description |
73
+ |--------|-------------|
74
+ | `PendulumComponentEditor` | Live state readout (θ, ω, t, E, \|ΔE/E₀\|) + Reset button |
75
+ | `DoublePendulumComponentEditor` | Dual-angle live readout + Reset button |
76
+
77
+ ---
78
+
79
+ ## Repository Structure
80
+
81
+ ```
82
+ mechanicsdsl-unity/
83
+ ├── Runtime/
84
+ │ ├── Components/
85
+ │ │ ├── PendulumComponent.cs
86
+ │ │ ├── DoublePendulumComponent.cs
87
+ │ │ └── CoupledPendulumsComponent.cs
88
+ │ ├── Utilities/
89
+ │ │ ├── ConservationMonitor.cs
90
+ │ │ ├── PhaseSpaceTrail.cs
91
+ │ │ └── MechanicsDSLMath.cs
92
+ │ └── com.mechanicsdsl.unity.runtime.asmdef
93
+ ├── Editor/
94
+ │ ├── PendulumComponentEditor.cs
95
+ │ ├── DoublePendulumComponentEditor.cs
96
+ │ └── com.mechanicsdsl.unity.editor.asmdef
97
+ ├── Tests/Runtime/
98
+ │ ├── TestPendulumEOM.cs
99
+ │ ├── TestDoublePendulumEOM.cs
100
+ │ └── MechanicsDSL.Tests.Runtime.asmdef
101
+ ├── Samples~/
102
+ │ ├── SimplePendulum/README.md
103
+ │ └── DoublePendulum/README.md
104
+ ├── docs/
105
+ │ ├── getting_started.md
106
+ │ ├── components_reference.md
107
+ │ └── adding_systems.md
108
+ └── package.json (UPM manifest)
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Installation
114
+
115
+ **Via UPM (Unity Package Manager):**
116
+
117
+ 1. Open **Window → Package Manager**
118
+ 2. Click **+** → **Add package from git URL**
119
+ 3. Enter: `https://github.com/MechanicsDSL/mechanicsdsl-unity.git`
120
+
121
+ ---
122
+
123
+ ## Quick Start
124
+
125
+ 1. Add **MechanicsDSL → Classical → Pendulum** to any GameObject
126
+ 2. Create a Sphere child and drag to **Bob Transform**
127
+ 3. Press Play — the sphere is driven by MechanicsDSL-generated Lagrangian equations
128
+
129
+ The Inspector shows live θ, ω, E, and |ΔE/E₀| during Play Mode. Click **Reset** to return to initial conditions without stopping.
130
+
131
+ ---
132
+
133
+ ## Running Tests
134
+
135
+ **Window → General → Test Runner → PlayMode → Run All**
136
+
137
+ Tests cover energy conservation (5–10 s), reset/initial conditions, equilibrium stability, and bob Transform updates.
138
+
139
+ ---
140
+
141
+ ## Generating New Components
142
+
143
+ ```bash
144
+ pip install mechanicsdsl-core
145
+ mechanicsdsl generate my_system.msl --target unity --out Assets/Physics/
146
+ ```
147
+
148
+ See [docs/adding_systems.md](docs/adding_systems.md) for the full workflow.
149
+
150
+ ---
151
+
152
+ ## Contributing
153
+
154
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
155
+
156
+ ## License
157
+
158
+ MIT License — see [LICENSE](LICENSE).