using Godot; using System; using Vim.Math3d; [Tool] public class OrbitSystem : Node, IMassive, ILocation { [Export] private NodePath _a; [Export] private NodePath _b; [Export] private NodePath _barycenter; private double _semiMajorAxis; [Export] private double SemiMajorAxis { get => _semiMajorAxis; set { _semiMajorAxis = value; if (!Engine.EditorHint) return; InvalidateGeometry(); } } private double _eccentricity; [Export(PropertyHint.Range, "0,1")] private double Eccentricity { get => _eccentricity; set { _eccentricity = value; if (!Engine.EditorHint) return; InvalidateGeometry(); } } #region Point Masses private IPointMass Primary { get { if (_pointMasses[0] == null) { InitPointMasses(); } return _pointMasses[0]; } } private IPointMass Secondary { get { if (_pointMasses[1] == null) { InitPointMasses(); } return _pointMasses[1]; } } private readonly IPointMass[] _pointMasses = new IPointMass[2]; private void InitPointMasses() { var a = GetNode(_a); var b = GetNode(_b); if (a.Mass > b.Mass) { _pointMasses[0] = a; _pointMasses[1] = b; } else { _pointMasses[0] = b; _pointMasses[1] = a; } } #endregion public float Mass => Primary.Mass + Secondary.Mass; public DVector3 Position { get => Barycenter; set => Barycenter = value; } public DVector3 Barycenter { get { var p0 = Primary.Position; var p1 = Secondary.Position; return p0.Lerp(p1, .5f); } // TODO - make setting the berycenter do something sensible? set => _ = value; } private Orbit _orbit = null; private Orbit Orbit { get { if (_orbit == null) { _orbit = new Orbit(); } return _orbit; } } private ImmediateGeometry _orbitGeometry = null; public ImmediateGeometry OrbitGeometry { get { if (_orbitGeometry == null) { _orbitGeometry = new ImmediateGeometry(); var m = new SpatialMaterial(); m.VertexColorUseAsAlbedo = true; m.FlagsUnshaded = true; _orbitGeometry.MaterialOverride = m; AddChild(_orbitGeometry); } return _orbitGeometry; } } private float _time = 0; [Export] private float _speed = 3f; public override void _Process(float delta) { _time += delta * _speed; InvalidateGeometry(); } private void InvalidateGeometry() { Orbit.Ellipse = new Ellipse(SemiMajorAxis, Eccentricity); Orbit.Draw(OrbitGeometry); Secondary.Position = Orbit.GetPosition(_time); } }