示例#1
0
 void Update()
 {
     if (!_sim)
     {
         _sim = GameObject.FindObjectOfType <SimulationControl>();
     }
     if (!_sim)
     {
         return;
     }
     if (!_srend)
     {
         CreateSpriteObj();
     }
     if (_srend)
     {
         if (!_cbody)
         {
             _cbody = GetComponent <CelestialBody>();
         }
         float maxrange = Mathf.Min(_sim.MaxAttractionRange, _cbody != null ? _cbody.MaxAttractionRange : _sim.MaxAttractionRange);
         if (maxrange > 100000f)
         {
             _srend.gameObject.SetActive(false);
             return;
         }
         else
         {
             _srend.gameObject.SetActive(true);
         }
         float radius = _srend.sprite.pixelsPerUnit / _srend.sprite.texture.width * maxrange * 2f;
         _srend.transform.localScale = new Vector3(radius / transform.localScale.x, radius / transform.localScale.y, 1 / transform.localScale.z);
     }
 }
示例#2
0
		void Update() {
			if ( !_sim){
				_sim = GameObject.FindObjectOfType<SimulationControl>();
			}
			if (!_sim){
				return;
			}
			if ( !_srend ) {
				CreateSpriteObj();
			}
			if ( _srend ) {
				if (!_cbody){
					_cbody = GetComponent<CelestialBody>();
				}
				float maxrange = Mathf.Min(_sim.MaxAttractionRange, _cbody != null ? _cbody.MaxAttractionRange : _sim.MaxAttractionRange);
				if ( maxrange > 100000f){
					_srend.gameObject.SetActive(false);
					return;
				}else{
					_srend.gameObject.SetActive(true);
				}
				float radius =  _srend.sprite.pixelsPerUnit / _srend.sprite.texture.width * maxrange * 2f;
				_srend.transform.localScale = new Vector3( radius / transform.localScale.x, radius / transform.localScale.y, 1 / transform.localScale.z );
			}
		}
示例#3
0
 void Start()
 {
     if (!SimControl)
     {
         SimControl = GameObject.FindObjectOfType <SimulationControl>();
     }
     if (!slider)
     {
         slider = GetComponentInChildren <Slider>();
     }
     if (slider)
     {
         slider.minValue = minValue;
         slider.maxValue = maxValue;
         if (SimControl)
         {
             slider.value = SimControl.TimeScale;
         }
         slider.onValueChanged.AddListener(( float f ) => { if (SimControl)
                                                            {
                                                                SimControl.TimeScale = f;
                                                            }
                                           });
     }
 }
示例#4
0
        protected override void Start()
        {
            base.Start();
            var transforms = GetComponentsInChildren <RectTransform>();

            if (Application.isPlaying)
            {
                leftFillArea = transforms.FirstOrDefault(t => t.name == "LeftFillArea");
                if (leftFillArea != null)
                {
                    leftFillArea.anchorMin = new Vector2(leftFillArea.anchorMax.x, 0);
                    leftFillArea.pivot     = new Vector2(1f, 0.5f);
                }
                rightFillArea = transforms.FirstOrDefault(t => t.name == "RightFillArea");
                if (rightFillArea != null)
                {
                    rightFillArea.anchorMax = new Vector2(rightFillArea.anchorMin.x, 1);
                    rightFillArea.pivot     = new Vector2(0f, 0.5f);
                }
                RefreshFillAreas();
                if (simControl == null)
                {
                    simControl = GameObject.FindObjectOfType <SimulationControl>();
                }
                if (simControl != null)
                {
                    simControl.timeScale = value;
                }
            }
        }
 /// <summary>
 /// Editor update delegate.
 /// Subscribe OnSceneGUI delegate if _simControl is not null. If SimulationControl is not exists on scene, continuously retry untill it's not created
 /// </summary>
 void StartupUpdate()
 {
     if (_waitTime > EditorApplication.timeSinceStartup)
     {
         return;                 ///Wait for check time
     }
     if (_simControl == null)
     {
         if (SimulationControl.instance != null)
         {
             _simControl = SimulationControl.instance;
         }
         else
         {
             var simControl = GameObject.FindObjectOfType <SimulationControl>();
             if (simControl != null)
             {
                 SimulationControl.instance = simControl;
                 _simControl = simControl;
             }
             else
             {
                 _waitTime = EditorApplication.timeSinceStartup + _waitDuration;
                 return;                         ///If simulation control is not created, exit and wait for next check time
             }
         }
     }
     _arrowsBtnImage = Resources.Load("Textures/arrowsBtn") as Texture2D;
     _orbitsBtnImage = Resources.Load("Textures/orbitsBtn") as Texture2D;
     // subscribe our OnSceneGUI for updates callbacks
     SceneView.onSceneGUIDelegate += this.OnSceneGUI;
     //Instance = this;
     SceneView.RepaintAll();
     EditorApplication.update -= StartupUpdate;             //Don't call this function any more.
 }
示例#6
0
		void Start() {
			if ( !SimControl ) {
				SimControl = GameObject.FindObjectOfType<SimulationControl>();
			}
			if ( !slider ) {
				slider = GetComponentInChildren<Slider>();
			}
			if ( slider ) {
				slider.minValue = minValue;
				slider.maxValue = maxValue;
				if ( SimControl ) {
					slider.value = SimControl.TimeScale;
				}
				slider.onValueChanged.AddListener( ( float f ) => { if ( SimControl ) { SimControl.TimeScale = f; } } );
			}
		}
示例#7
0
        ///<summary>Computes time step to advance hydraulic simulation.</summary>
        private long TimeStep()
        {
            long tstep = net.HStep;

            long n = (Htime + net.PStart) / net.PStep + 1;
            long t = n * net.PStep - Htime;

            if (t > 0 && t < tstep)
            {
                tstep = t;
            }

            // Revise time step based on smallest time to fill or drain a tank
            t = Rtime - Htime;
            if (t > 0 && t < tstep)
            {
                tstep = t;
            }

            tstep = SimulationTank.MinimumTimeStep(_tanks, tstep);
            tstep = SimulationControl.MinimumTimeStep(net, _controls, Htime, tstep);

            if (_rules.Length > 0)
            {
                long step, htime;

                SimulationRule.MinimumTimeStep(
                    net,
                    _logger,
                    _rules,
                    _tanks,
                    Htime,
                    tstep,
                    _dsystem,
                    out step,
                    out htime);

                tstep = step;
                Htime = htime;
            }
            else
            {
                SimulationTank.StepWaterLevels(_tanks, net.FieldsMap, tstep);
            }

            return(tstep);
        }
 void FindReferences()
 {
     if (simControl == null)
     {
         simControl = GameObject.FindObjectOfType <SimulationControl>();
     }
     if (cam == null)
     {
         cam = Camera.main ?? GameObject.FindObjectOfType <Camera>();
     }
     if (pool == null)
     {
         pool = GetComponent <BodiesPool>() ?? GameObject.FindObjectOfType <BodiesPool>();
     }
     if (predictionSystem == null)
     {
         predictionSystem = GameObject.FindObjectOfType <PredictionSystem>();
     }
 }
    private void ProcessPacket(byte[] packet)
    {
        using (MemoryStream stream = new MemoryStream(packet))
        {
            using (BinaryReader reader = new BinaryReader(stream))
            {
                var type = reader.ReadSarlString();

                switch (type)
                {
                case SimulationControl.SIMULATION_CONTROL:
                {
                    var simulationControl = new SimulationControl();
                    simulationControl.Parse(reader.BaseStream);
                    SimulationControlReceived(simulationControl);
                    Debug.Log("Simulation Control received");
                }
                break;

                case Influence.PHYSICAL_INFLUENCE:
                {
                    var influence = new PhysicalInfluence();
                    influence.Parse(reader.BaseStream);
                    PhysicalInfluenceReceived(influence);
                    Debug.Log("Physical Influence received");
                }
                break;

                case Influence.ACTION_INFLUENCE:
                {
                    var influence = new ActionInfluence();
                    influence.Parse(reader.BaseStream);
                    ActionInfluenceReceived(influence);
                    Debug.Log("Action Influence received");
                }
                break;
                }
            }
        }
    }
示例#10
0
        ///<summary>Solve the linear equation system to compute the links flows and nodes heads.</summary>
        /// <returns>Solver steps and relative error.</returns>
        protected void NetSolve(out int iter, out double relerr)
        {
            iter   = 0;
            relerr = 0.0;

            int nextCheck = net.CheckFreq;

            if (net.StatFlag == StatFlag.FULL)
            {
                LogRelErr(iter, relerr);
            }

            int maxTrials = net.MaxIter;

            if (net.ExtraIter > 0)
            {
                maxTrials += net.ExtraIter;
            }

            double relaxFactor = 1.0;
            int    errcode     = 0;

            iter = 1;

            while (iter <= maxTrials)
            {
                //Compute coefficient matrices A & F and solve A*H = F
                // where H = heads, A = Jacobian coeffs. derived from
                // head loss gradients, & F = flow correction terms.
                NewCoeffs();

                //dumpMatrixCoeffs(new File("dumpMatrix.txt"),true);

                // Solution for H is returned in F from call to linsolve().
                errcode = _smat.LinSolve(
                    _junctions.Count,
                    _lsv.AiiVector,
                    _lsv.AijVector,
                    _lsv.RhsCoeffs);

                // Ill-conditioning problem
                if (errcode > 0)
                {
                    // If control valve causing problem, fix its status & continue,
                    // otherwise end the iterations with no solution.
                    if (SimulationValve.CheckBadValve(
                            net,
                            _logger,
                            _valves,
                            Htime,
                            _smat.GetOrder(errcode)))
                    {
                        continue;
                    }

                    break;
                }

                // Update current solution.
                // (Row[i] = row of solution matrix corresponding to node i).
                foreach (SimulationNode node  in  _junctions)
                {
                    node.SimHead = _lsv.GetRhsCoeff(_smat.GetRow(node.Index)); // Update heads
                }

                // Update flows
                relerr = NewFlows(relaxFactor);

                // Write convergence error to status report if called for
                if (net.StatFlag == StatFlag.FULL)
                {
                    LogRelErr(iter, relerr);
                }

                relaxFactor = 1.0;

                bool valveChange = false;

                //  Apply solution damping & check for change in valve status
                if (net.DampLimit > 0.0)
                {
                    if (relerr <= net.DampLimit)
                    {
                        relaxFactor = 0.6;
                        valveChange = SimulationValve.ValveStatus(net, _logger, _valves);
                    }
                }
                else
                {
                    valveChange = SimulationValve.ValveStatus(net, _logger, _valves);
                }

                // Check for convergence
                if (relerr <= net.HAcc)
                {
                    //  We have convergence. Quit if we are into extra iterations.
                    if (iter > net.MaxIter)
                    {
                        break;
                    }

                    //  Quit if no status changes occur.
                    bool statChange = valveChange;

                    if (SimulationLink.LinkStatus(net, _logger, _links))
                    {
                        statChange = true;
                    }

                    if (SimulationControl.PSwitch(_logger, net, _controls))
                    {
                        statChange = true;
                    }

                    if (!statChange)
                    {
                        break;
                    }

                    //  We have a status change so continue the iterations
                    nextCheck = iter + net.CheckFreq;
                }
                else if (iter <= net.MaxCheck && iter == nextCheck)
                {
                    // No convergence yet. See if its time for a periodic status
                    // check  on pumps, CV's, and pipes connected to tanks.
                    SimulationLink.LinkStatus(net, _logger, _links);
                    nextCheck += net.CheckFreq;
                }

                iter++;
            }


            foreach (SimulationNode node  in  _junctions)
            {
                node.SimDemand = node.SimDemand + node.SimEmitter;
            }

            if (errcode > 0)
            {
                LogHydErr(_smat.GetOrder(errcode));
                errcode = 110;
                return;
            }

            if (errcode != 0)
            {
                throw new ENException((ErrorCode)errcode);
            }
        }
		/// <summary>
		/// Editor update delegate. 
		/// Subscribe OnSceneGUI delegate if _simControl is not null. If SimulationControl is not exists on scene, continuously retry untill it's not created
		/// </summary>
		void StartupUpdate() {
			if (_waitTime > EditorApplication.timeSinceStartup) {
				return; ///Wait for check time
			}
			if (_simControl == null) {
				if (SimulationControl.instance != null) {
					_simControl = SimulationControl.instance;
				}
				else {
					var simControl = GameObject.FindObjectOfType<SimulationControl>();
					if (simControl != null) {
						SimulationControl.instance = simControl;
						_simControl = simControl;
					}
					else {
						_waitTime = EditorApplication.timeSinceStartup + _waitDuration;
						return; ///If simulation control is not created, exit and wait for next check time
					}
				}
			}
			_arrowsBtnImage = Resources.Load("Textures/arrowsBtn") as Texture2D;
			_orbitsBtnImage = Resources.Load("Textures/orbitsBtn") as Texture2D;
			// subscribe our OnSceneGUI for updates callbacks
			SceneView.onSceneGUIDelegate += this.OnSceneGUI;
			//Instance = this;
			SceneView.RepaintAll();
			EditorApplication.update -= StartupUpdate; //Don't call this function any more.
		}
		/// <summary>
		/// simControl may be null
		/// </summary>
		public SceneViewDisplayManager(SimulationControl simControl) {
			_simControl = simControl;
			_bodies = new List<CelestialBody>();
			EditorApplication.update += StartupUpdate;
		}
		/// <summary>
		/// General window onGUI
		/// </summary>
		void OnGUI() {
			if (!_simControl || _simControlSerialized == null) {
				_simControl = FindOrCreateSimulationControlGameObject();
				if (!_simControl) {//check if creation process failed
					Debug.LogWarning("SpaceGravity2D.Editor: no Simulation Control found or created");
					this.Close();
					return;
				}
				InitializeProperties();
			}
			if (SimulationControl.instance == null) {
				SimulationControl.instance = _simControl;
			}
			_simControlSerialized.Update();
			EditorGUILayout.LabelField("Tools:", EditorStyles.boldLabel);
			if (GUILayout.Button("Inverse velocity for all selected celestial bodies")) {
				InverseVelocityFor(Selection.gameObjects); //Undo functionality is supported
			}
			if (GUILayout.Button(new GUIContent("Place all selected bodies randomly around attractor", "Convinient to create asteroids belts. \nusage: all selected bodies must have same attracor. before making selection and pressing this button, place one body at preffered minimal distance and one body - at preffered max distance. placement distances bounds will be calculated from bodies start positions."))) {
				PlaceBodiesRandomly(Selection.gameObjects);
			}
			try {
				EditorGUILayout.LabelField("Global Parameters:", EditorStyles.boldLabel);
				EditorGUILayout.PropertyField(calcTypeProp, new GUIContent("N-Body algorithm", "Euler - fastest, \nVerlet - fast and more stable, \nRungeKutta - more precise"));
				EditorGUILayout.PropertyField(gravConstProp);
				var gravConst = EditorGUILayout.FloatField(new GUIContent("Grav.Const. Proportional", "Change gravitational constant AND keep all orbits unaffected"), gravConstProp.floatValue);
				if (gravConst != gravConstProp.floatValue) {
					if (Mathf.Abs(gravConst) < 1e-8f) {
						gravConst = 1e-4f;
					}
					_simControl.GravitationalConstantProportional = gravConst;
				}
				EditorGUILayout.PropertyField(inflRangeProp);
				EditorGUILayout.PropertyField(inflRangeMinProp);
				EditorGUILayout.PropertyField(timeScaleProp);
				EditorGUILayout.PropertyField(minMassProp);
				EditorGUILayout.PropertyField(drawArrowsProp);
				EditorGUILayout.PropertyField(drawEditorOrbsProp);
				EditorGUILayout.PropertyField(drawDisabledOrbsProp, new GUIContent("Draw disabled orbits in editor", "Use if you want to see all orbits, even if celestial body has disabled orbit display"));
				EditorGUILayout.PropertyField(drawPlayOrbsProp);
				EditorGUILayout.LabelField("Orbit Line Renderer:", EditorStyles.boldLabel);
				EditorGUILayout.PropertyField(lineRendMatProp);
				EditorGUILayout.PropertyField(orbitWidthProp);
				EditorGUILayout.PropertyField(orbitPointsProp);
				EditorGUILayout.LabelField("Editor Parameters:", EditorStyles.boldLabel);
				EditorGUILayout.PropertyField(drawDebugPointsProp);
				EditorGUILayout.PropertyField(arrowsSizeProp);
				EditorGUILayout.PropertyField(arrowsGlobalProp, new GUIContent("Global velocity vectors", "Toggle global/local space for velocity arrows"));
				_simControl.selectWhenDraggingArrow = EditorGUILayout.Toggle("Select object with arrow", _simControl.selectWhenDraggingArrow);
			}
			catch (Exception ex) {
				Debug.LogError("SpaceGravity2D.Editor: " + ex.Message);
				Close();
			}
			if (GUI.changed) {
				_simControlSerialized.ApplyModifiedProperties();
				SceneView.RepaintAll();
			}
		}
示例#14
0
 ///<summary>Implements simple controls based on time or tank levels.</summary>
 private void ComputeControls()
 {
     SimulationControl.StepActions(_logger, net, _controls, Htime);
 }
示例#15
0
 void Start()
 {
     simulationControl = GetComponent <SimulationControl>();
 }
 /// <summary>
 /// simControl may be null
 /// </summary>
 public SceneViewDisplayManager(SimulationControl simControl)
 {
     _simControl = simControl;
     _bodies     = new List <CelestialBody>();
     EditorApplication.update += StartupUpdate;
 }
 /// <summary>
 /// General window onGUI
 /// </summary>
 void OnGUI()
 {
     if (!_simControl || _simControlSerialized == null)
     {
         _simControl = FindOrCreateSimulationControlGameObject();
         if (!_simControl)                  //check if creation process failed
         {
             Debug.LogWarning("SpaceGravity2D.Editor: no Simulation Control found or created");
             this.Close();
             return;
         }
         InitializeProperties();
     }
     if (SimulationControl.instance == null)
     {
         SimulationControl.instance = _simControl;
     }
     _simControlSerialized.Update();
     EditorGUILayout.LabelField("Tools:", EditorStyles.boldLabel);
     if (GUILayout.Button("Inverse velocity for all selected celestial bodies"))
     {
         InverseVelocityFor(Selection.gameObjects);                 //Undo functionality is supported
     }
     if (GUILayout.Button(new GUIContent("Place all selected bodies randomly around attractor", "Convinient to create asteroids belts. \nusage: all selected bodies must have same attracor. before making selection and pressing this button, place one body at preffered minimal distance and one body - at preffered max distance. placement distances bounds will be calculated from bodies start positions.")))
     {
         PlaceBodiesRandomly(Selection.gameObjects);
     }
     try {
         EditorGUILayout.LabelField("Global Parameters:", EditorStyles.boldLabel);
         EditorGUILayout.PropertyField(calcTypeProp, new GUIContent("N-Body algorithm", "Euler - fastest, \nVerlet - fast and more stable, \nRungeKutta - more precise"));
         EditorGUILayout.PropertyField(gravConstProp);
         var gravConst = EditorGUILayout.FloatField(new GUIContent("Grav.Const. Proportional", "Change gravitational constant AND keep all orbits unaffected"), gravConstProp.floatValue);
         if (gravConst != gravConstProp.floatValue)
         {
             if (Mathf.Abs(gravConst) < 1e-8f)
             {
                 gravConst = 1e-4f;
             }
             _simControl.GravitationalConstantProportional = gravConst;
         }
         EditorGUILayout.PropertyField(inflRangeProp);
         EditorGUILayout.PropertyField(inflRangeMinProp);
         EditorGUILayout.PropertyField(timeScaleProp);
         EditorGUILayout.PropertyField(minMassProp);
         EditorGUILayout.PropertyField(drawArrowsProp);
         EditorGUILayout.PropertyField(drawEditorOrbsProp);
         EditorGUILayout.PropertyField(drawDisabledOrbsProp, new GUIContent("Draw disabled orbits in editor", "Use if you want to see all orbits, even if celestial body has disabled orbit display"));
         EditorGUILayout.PropertyField(drawPlayOrbsProp);
         EditorGUILayout.LabelField("Orbit Line Renderer:", EditorStyles.boldLabel);
         EditorGUILayout.PropertyField(lineRendMatProp);
         EditorGUILayout.PropertyField(orbitWidthProp);
         EditorGUILayout.PropertyField(orbitPointsProp);
         EditorGUILayout.LabelField("Editor Parameters:", EditorStyles.boldLabel);
         EditorGUILayout.PropertyField(drawDebugPointsProp);
         EditorGUILayout.PropertyField(arrowsSizeProp);
         EditorGUILayout.PropertyField(arrowsGlobalProp, new GUIContent("Global velocity vectors", "Toggle global/local space for velocity arrows"));
         _simControl.selectWhenDraggingArrow = EditorGUILayout.Toggle("Select object with arrow", _simControl.selectWhenDraggingArrow);
     }
     catch (Exception ex) {
         Debug.LogError("SpaceGravity2D.Editor: " + ex.Message);
         Close();
     }
     if (GUI.changed)
     {
         _simControlSerialized.ApplyModifiedProperties();
         SceneView.RepaintAll();
     }
 }
示例#18
0
        /// <summary>
        /// Editor window onGUI.
        /// </summary>
        private void OnGUI()
        {
            if (!_simControl || _simControlSerialized == null)
            {
                _simControl = FindSimulationControlGameObject();
                if (!_simControl)
                {
                    EditorGUILayout.LabelField("SimulationControl instance not found on scene.");
                    return;
                }
                InitializeProperties();
            }

            if (SimulationControl.Instance == null)
            {
                SimulationControl.Instance = _simControl;
            }

            _simControlSerialized.Update();
            _scrollPos = GUILayout.BeginScrollView(_scrollPos, false, true, GUILayout.MinHeight(200), GUILayout.MaxHeight(1000), GUILayout.ExpandHeight(true));
            EditorGUILayout.LabelField("Global Parameters:", EditorStyles.boldLabel);
            EditorGUILayout.PropertyField(calcTypeProp, new GUIContent("N-Body algorithm(?)", "Euler - fastest performance, \nVerlet - fast and more stable, \nRungeKutta - more precise."));

            var gravConst = EditorGUILayout.DoubleField(new GUIContent("Gravitational Constant(?)", "Main constant. The real value 6.67384 * 10E-11 may not be very useful for gaming purposes."), _simControl.GravitationalConstant);

            if (gravConst != _simControl.GravitationalConstant)
            {
                Undo.RecordObject(_simControl, "Grav.Const change");
                _simControl.GravitationalConstant = gravConst;
            }

            EditorGUILayout.Space();
            EditorGUILayout.BeginVertical("box");
            {
                EditorGUILayout.LabelField("Change Grav. Const. with proportional adjusting of all velocities on scene:");
                gravConst = EditorGUILayout.DoubleField(new GUIContent("Grav.Const.Proportional(?)", "Change gravitational constant AND keep all orbits unaffected."), _simControl.GravitationalConstant);
                if (gravConst != _simControl.GravitationalConstant)
                {
                    Undo.RecordObject(_simControl, "Grav.Const change");
                    _simControl.GravitationalConstantProportional = gravConst;
                }
            }

            EditorGUILayout.EndVertical();
            EditorGUILayout.Space();
            EditorGUILayout.PropertyField(inflRangeProp, new GUIContent("Max influence range(?)", "Global max range of n-body attraction."));
            EditorGUILayout.PropertyField(inflRangeMinProp, new GUIContent("Min influence range(?)", "Global min range of n-body attraction."));
            EditorGUILayout.PropertyField(timeScaleProp, new GUIContent("Time Scale(?)", "Time multiplier. Note: high value will decrease n-body calculations precision."));
            EditorGUILayout.PropertyField(minMassProp, new GUIContent("Min attractor mass(?)", "Mass threshold for body to became attractor."));
            EditorGUILayout.Space();
            EditorGUILayout.Space();
            EditorGUILayout.LabelField("Keep ALL bodies on ecliptic plane:");
            var keep2d = EditorGUILayout.Toggle(new GUIContent("2d mode(?)", "Force all bodies to project positions and velocities onto ecliptic plane."), _simControl.KeepBodiesOnEclipticPlane);

            if (keep2d != _simControl.KeepBodiesOnEclipticPlane)
            {
                Undo.RecordObject(_simControl, "2d mode toggle");
                _simControl.KeepBodiesOnEclipticPlane = keep2d;
                if (keep2d)
                {
                    _simControl.ProjectAllBodiesOnEcliptic();
                }
            }

            EditorGUILayout.LabelField("Is affected by global timescale:");
            var scaledTime = EditorGUILayout.Toggle(new GUIContent("Global timescale(?)", "Toggle ignoring of Time.timeScale."), _simControl.AffectedByGlobalTimescale);

            if (scaledTime != _simControl.AffectedByGlobalTimescale)
            {
                Undo.RecordObject(_simControl, "affected by timescale toggle");
                _simControl.AffectedByGlobalTimescale = scaledTime;
            }

            EditorGUILayout.Space();
            EditorGUILayout.PropertyField(eclipticNormalProp, new GUIContent("Ecliptic Normal Vector(?)", "Perpendicular to ecliptic plane."));
            EditorGUILayout.PropertyField(eclipticUpProp, new GUIContent("Ecliptic Up Vector(?)", "Up vector on ecliptic plane. Always perpendicular to ecliptic normal. Used for rotation tool."));
            EditorGUILayout.Space();
            EditorGUILayout.LabelField("Set ecliptic normal along axis:", EditorStyles.boldLabel);
            GUILayout.BeginHorizontal();
            if (GUILayout.Button("X"))
            {
                SetEclipticNormal(new Vector3d(1, 0, 0), new Vector3d(0, 0, 1));
            }

            GUILayout.Space(6);
            if (GUILayout.Button("-X"))
            {
                SetEclipticNormal(new Vector3d(-1, 0, 0), new Vector3d(0, 0, -1));
            }

            GUILayout.EndHorizontal();
            GUILayout.BeginHorizontal();
            if (GUILayout.Button("Y"))
            {
                SetEclipticNormal(new Vector3d(0, 1, 0), new Vector3d(0, 0, 1));
            }

            GUILayout.Space(6);
            if (GUILayout.Button("-Y"))
            {
                SetEclipticNormal(new Vector3d(0, -1, 0), new Vector3d(0, 0, -1));
            }

            GUILayout.EndHorizontal();
            GUILayout.BeginHorizontal();
            if (GUILayout.Button("Z"))
            {
                SetEclipticNormal(new Vector3d(0, 0, 1), new Vector3d(1, 0, 0));
            }

            GUILayout.Space(6);
            if (GUILayout.Button("-Z"))
            {
                SetEclipticNormal(new Vector3d(0, 0, -1), new Vector3d(-1, 0, 0));
            }

            GUILayout.EndHorizontal();
            EditorGUILayout.LabelField("Ecliptic plane is used to calculate ascending/descending nodes,");
            EditorGUILayout.LabelField("orbit inclination and for 2D-restricted simulation (if this option is active).");
            bool eclipticRotateTool = GUILayout.Toggle(_displayManager.IsEclipticRotating, "Rotate Ecliptic Plane", "Button");

            if (eclipticRotateTool != _displayManager.IsEclipticRotating)
            {
                _displayManager.IsEclipticRotating = eclipticRotateTool;
            }

            bool orbitRotateTool = GUILayout.Toggle(_displayManager.IsOrbitRotating, "Rotate Orbit Of Selected Obj.", "Button");

            if (orbitRotateTool != _displayManager.IsOrbitRotating)
            {
                _displayManager.IsOrbitRotating = orbitRotateTool;
            }

            EditorGUIUtility.labelWidth = 250f;
            EditorGUILayout.BeginVertical("box");
            {
                EditorGUILayout.PropertyField(sceneViewDisplayParametersProp, true);
            }

            EditorGUILayout.EndVertical();
            GUILayout.Space(15);
            GUILayout.EndScrollView();
            if (GUI.changed)
            {
                _simControlSerialized.ApplyModifiedProperties();
                SceneView.RepaintAll();
            }
        }