public override void OnInspectorGUI()
    {
        GUI.changed = false;
        BinaryPair bPair    = (BinaryPair)target;
        Vector3    velocity = Vector3.zero;

        GravityScaler.Units units = GravityEngine.Instance().units;
        string prompt             = string.Format("Velocity ({0})", GravityScaler.VelocityUnits(units));

        velocity = EditorGUILayout.Vector3Field(new GUIContent(prompt, "velocity of binary center of mass"), bPair.velocity);


        if (GUI.changed)
        {
            Undo.RecordObject(bPair, "EllipseBase Change");
            bPair.velocity = velocity;
            EditorUtility.SetDirty(bPair);
        }
        base.OnInspectorGUI();

        if (axisUpdated)
        {
            bPair.ApplyScale(GravityEngine.Instance().GetLengthScale());
        }
    }
    public override void OnInspectorGUI()
    {
        GUI.changed = false;

        GravityParticles nbp              = (GravityParticles)target;
        Vector3          initialV         = nbp.initialVelocity;
        bool             addNBodyVelocity = nbp.addNBodyVelocity;

        IGravityParticlesInit particlesInit = nbp.GetComponent <IGravityParticlesInit>();

        GravityScaler.Units units = GravityEngine.Instance().units;
        string prompt             = string.Format("Initial Vel. ({0})", GravityScaler.VelocityUnits(units));

        if (particlesInit == null)
        {
            EditorGUILayout.LabelField("No init script", EditorStyles.boldLabel);
            bool hasNBody = false;
            if (nbp.GetComponent <NBody>() != null)
            {
                hasNBody = true;
            }
            else if (nbp.transform.parent != null && nbp.transform.parent.gameObject.GetComponent <NBody>() != null)
            {
                hasNBody = true;
            }
            if (hasNBody)
            {
                addNBodyVelocity = EditorGUILayout.Toggle(new GUIContent("Use velocity from NBody", velTip), nbp.addNBodyVelocity);
                if (addNBodyVelocity)
                {
                    EditorGUILayout.LabelField("Initial Velocity: (from NBody)", EditorStyles.boldLabel);
                }
                else
                {
                    initialV = EditorGUILayout.Vector3Field(new GUIContent(prompt, iTip), nbp.initialVelocity);
                }
            }
            else
            {
                initialV = EditorGUILayout.Vector3Field(new GUIContent(prompt, iTip), nbp.initialVelocity);
            }
        }
        else
        {
            EditorGUILayout.LabelField("Initalize with: " + particlesInit.GetType().ToString(), EditorStyles.boldLabel);
        }

        if (GUI.changed)
        {
            Undo.RecordObject(nbp, "GravityParticles Change");
            nbp.initialVelocity  = initialV;
            nbp.addNBodyVelocity = addNBodyVelocity;
            EditorUtility.SetDirty(nbp);
        }
    }
    public override void OnInspectorGUI()
    {
        GUI.changed = false;

        NBodyCollision nbc        = (NBodyCollision)target;
        GameObject     explodePF  = nbc.explosionPrefab;
        int            precedence = nbc.collisionPrecedence;
        float          explodeOrBounceVelocity = nbc.explodeOrBounceVelocity;
        float          bounceFactor            = 1f;
        string         layer = null;

        float oldWidth = EditorGUIUtility.labelWidth;

        EditorGUIUtility.labelWidth = 200;

        NBodyCollision.CollisionType type = NBodyCollision.CollisionType.ABSORB_IMMEDIATE;

        type       = (NBodyCollision.CollisionType)EditorGUILayout.EnumPopup(new GUIContent("Collision Type", collTip), nbc.collisionType);
        precedence = EditorGUILayout.IntField(new GUIContent("Collision Precedence", cTip), nbc.collisionPrecedence);

        layer = EditorGUILayout.TextField(new GUIContent("Collision Layer (Optional)", layerTip), nbc.collisionLayer);

        if (type == NBodyCollision.CollisionType.EXPLODE)
        {
            explodePF = (GameObject)EditorGUILayout.ObjectField(
                new GUIContent("Explosion Prefab", "Particle System with NBodyParticles"), explodePF, typeof(GameObject), true);
        }
        else if (type == NBodyCollision.CollisionType.BOUNCE)
        {
            EditorGUIUtility.labelWidth = oldWidth;
            bounceFactor = EditorGUILayout.Slider(new GUIContent("Bounce", bTip), nbc.bounceFactor, 0f, 1f);
        }
        else if (type == NBodyCollision.CollisionType.EXPLODE_OR_BOUNCE)
        {
            explodePF = (GameObject)EditorGUILayout.ObjectField(
                new GUIContent("Explosion Prefab", "Particle System with NBodyParticles"), explodePF, typeof(GameObject), true);
            bounceFactor = EditorGUILayout.Slider(new GUIContent("Bounce", bTip), nbc.bounceFactor, 0f, 1f);
            GravityScaler.Units units = GravityEngine.Instance().units;
            EditorGUIUtility.labelWidth = oldWidth;
            string prompt = string.Format("Relative Velocity to Explode ({0})", GravityScaler.VelocityUnits(units));
            explodeOrBounceVelocity = EditorGUILayout.FloatField(new GUIContent(prompt, dTip), nbc.explodeOrBounceVelocity);
        }
        if (GUI.changed)
        {
            Undo.RecordObject(nbc, "NBodyCollision Change");
            nbc.explosionPrefab         = explodePF;
            nbc.collisionType           = type;
            nbc.bounceFactor            = bounceFactor;
            nbc.explodeOrBounceVelocity = explodeOrBounceVelocity;
            nbc.collisionPrecedence     = precedence;
            nbc.collisionLayer          = layer;
            EditorUtility.SetDirty(nbc);
        }
    }
Beispiel #4
0
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.A))
        {
            fromPhase += PHASE_PER_KEY;
        }
        else if (Input.GetKey(KeyCode.S))
        {
            fromPhase -= PHASE_PER_KEY;
        }
        else if (Input.GetKey(KeyCode.Q))
        {
            toPhase += PHASE_PER_KEY;
        }
        else if (Input.GetKey(KeyCode.W))
        {
            toPhase -= PHASE_PER_KEY;
        }
        fromPhase = NUtils.DegreesMod360(fromPhase);
        toPhase   = NUtils.DegreesMod360(toPhase);
        SetMarkers();

        shipOrbitData.SetOrbitForVelocity(spaceshipNBody, shipOrbit.centerNbody);

        // Determine TOF
        Vector3d from = new Vector3d(shipOrbitData.GetPhysicsPositionforEllipse(fromPhase));
        Vector3d to   = new Vector3d(shipOrbitData.GetPhysicsPositionforEllipse(toPhase));

        Vector3d shipPos = GravityEngine.instance.GetPositionDoubleV3(spaceshipNBody);
        double   tPeri   = shipOrbit.TimeOfFlight(shipPos, new Vector3d(shipOrbitData.GetPhysicsPositionforEllipse(0f)));
        double   tApo    = shipOrbit.TimeOfFlight(shipPos, new Vector3d(shipOrbitData.GetPhysicsPositionforEllipse(180f)));
        double   tof     = shipOrbit.TimeOfFlight(from, to);

        // Scale to game time
        //tApo = GravityScaler.ScaleToGameSeconds((float) tApo);
        //tPeri = GravityScaler.ScaleToGameSeconds((float)tPeri);
        //tof = GravityScaler.ScaleToGameSeconds((float)tof);
        //tofText.text = string.Format("Time of Flight = {0:#.#}\nTime to Apoapsis = {1:#.#}\nTime to Periapsis = {2:#.#}\ntau = {3}",
        //    tof, tApo, tPeri, shipOrbitData.tau);
        GravityScaler.Units units = GravityEngine.instance.units;
        tofText.text = string.Format("Time of Flight = {0}\nTime to Apoapsis = {1}\nTime to Periapsis = {2}\ntau = {3}",
                                     GravityScaler.GetWorldTimeFormatted(tof, units),
                                     GravityScaler.GetWorldTimeFormatted(tApo, units),
                                     GravityScaler.GetWorldTimeFormatted(tPeri, units),
                                     GravityScaler.GetWorldTimeFormatted(shipOrbitData.tau, units));
    }
Beispiel #5
0
    public override void OnInspectorGUI()
    {
        GUI.changed = false;
        GravityEngine gravityEngine = (GravityEngine)target;
        float         massScale     = gravityEngine.massScale;
        float         timeScale     = gravityEngine.timeScale;
        float         lengthScale   = gravityEngine.lengthScale;

        float physToWorldFactor = gravityEngine.physToWorldFactor;

        bool optimizeMassless = gravityEngine.optimizeMassless;
        bool detectNbodies    = gravityEngine.detectNbodies;
        bool evolveAtStart    = gravityEngine.evolveAtStart;
        bool scaling          = gravityEngine.editorShowScale;
        bool showAdvanced     = gravityEngine.editorShowAdvanced;
        bool showTrajectory   = gravityEngine.editorShowTrajectory;
        bool cmFoldout        = gravityEngine.editorCMfoldout;
        bool mapToScene       = gravityEngine.mapToScene;

        bool       trajectoryPrediction = gravityEngine.trajectoryPrediction;
        float      trajectoryTime       = gravityEngine.trajectoryTime;
        GameObject trajCanvas           = gravityEngine.trajectoryCanvas;
        GameObject markerParent         = gravityEngine.markerParent;
        float      computeFactor        = gravityEngine.trajectoryComputeFactor;

        int stepsPerFrame         = gravityEngine.stepsPerFrame;
        int particleStepsPerFrame = gravityEngine.particleStepsPerFrame;

        GravityEngine.Algorithm algorithm = GravityEngine.Algorithm.LEAPFROG;
        ForceChooser.Forces     force     = ForceChooser.Forces.Gravity;
        GravityScaler.Units     units     = GravityScaler.Units.DIMENSIONLESS;

        EditorGUIUtility.labelWidth = 200;

        mapToScene = EditorGUILayout.Toggle(new GUIContent("Use Transform to Reposition", mapTip), mapToScene);

        EditorGUIUtility.labelWidth = 150;

        // SCALING
        scaling = EditorGUILayout.Foldout(scaling, "Scaling");
        if (scaling)
        {
            units =
                (GravityScaler.Units)EditorGUILayout.EnumPopup(new GUIContent("Units", unitsTip), gravityEngine.units);
            EditorGUILayout.LabelField("Scaling values change when <ENTER> ispressed.");

            switch (units)
            {
            case GravityScaler.Units.DIMENSIONLESS:
                // only have mass scale in DL case
                EditorGUILayout.LabelField("Use mass scale to control scene speed.");
                EditorGUILayout.LabelField("(More massive = faster)");
                massScale = EditorGUILayout.DelayedFloatField(new GUIContent("Mass Scale", mTip), gravityEngine.massScale);
                break;

            case GravityScaler.Units.SI:
                // no mass scale is controlled by time exclusivly
                EditorGUILayout.LabelField("m/kg/sec.");
                // meters per Unity unit in the case of meters
                lengthScale = EditorGUILayout.DelayedFloatField(new GUIContent("Unity unit per m", timeTip), gravityEngine.lengthScale);
                timeScale   = EditorGUILayout.DelayedFloatField(new GUIContent("Game sec. per sec.", timeTip), gravityEngine.timeScale);
                break;

            case GravityScaler.Units.ORBITAL:
                EditorGUILayout.LabelField("km/1E24 kg/hr");
                // Express in km per Unity unit km/U
                lengthScale = EditorGUILayout.DelayedFloatField(new GUIContent("Unity unit per km", timeTip), gravityEngine.lengthScale);
                timeScale   = EditorGUILayout.DelayedFloatField(new GUIContent("Game sec. per hour", timeTip), gravityEngine.timeScale);
                break;

            case GravityScaler.Units.SOLAR:
                EditorGUILayout.LabelField("AU/1E24 kg/year");
                lengthScale = EditorGUILayout.DelayedFloatField(new GUIContent("Unity unit per AU", timeTip), gravityEngine.lengthScale);
                timeScale   = EditorGUILayout.DelayedFloatField(new GUIContent("Game Sec per year", timeTip), gravityEngine.timeScale);
                break;
            }
        }

        // TRAJECTORY PREDICTION
        showTrajectory = EditorGUILayout.Foldout(showTrajectory, "Trajectory Prediction");
        if (showTrajectory)
        {
            trajectoryPrediction = EditorGUILayout.Toggle(new GUIContent("Trajectory Prediction", trajTip), trajectoryPrediction);
            trajectoryTime       = EditorGUILayout.FloatField(new GUIContent("Trajectory Time", trajTimeTip), trajectoryTime);
            computeFactor        = EditorGUILayout.FloatField(new GUIContent("Re-computes per frame)", trajResetTip), computeFactor);
            trajCanvas           = (GameObject)EditorGUILayout.ObjectField(new GUIContent("Canvas for Text (optional)", trajCanvasTip),
                                                                           trajCanvas, typeof(GameObject), true);
            markerParent = (GameObject)EditorGUILayout.ObjectField(new GUIContent("Time Marker Parent (optional)", markerTip),
                                                                   markerParent, typeof(GameObject), true);
        }

        // ADVANCED
        showAdvanced = EditorGUILayout.Foldout(showAdvanced, "Advanced");
        if (showAdvanced)
        {
            algorithm =
                (GravityEngine.Algorithm)EditorGUILayout.EnumPopup(new GUIContent("Algorithm", algoTip), gravityEngine.algorithm);

            // Force selection is tangled with choice of integrator and scale - so needs to be here
            force =
                (ForceChooser.Forces)EditorGUILayout.EnumPopup(new GUIContent("Force", forceTip), gravityEngine.force);
            if (force == ForceChooser.Forces.Custom)
            {
                IForceDelegate force_delegate = gravityEngine.GetComponent <IForceDelegate>();
                if (force_delegate == null)
                {
                    EditorGUILayout.LabelField("  Attach a Force Delegate to this object.", EditorStyles.boldLabel);
                }
                else
                {
                    EditorGUILayout.LabelField("  Force delegate: " + force_delegate.GetType());
                }
            }
            if (force != ForceChooser.Forces.Gravity)
            {
                EditorGUILayout.LabelField("    Note: Orbit predictors assume Newtonian gravity");
                EditorGUILayout.LabelField("    They are not accurate for other forces.");
            }

            optimizeMassless = EditorGUILayout.Toggle(new GUIContent("Optimize Massless Bodies", mlessTip),
                                                      gravityEngine.optimizeMassless);
            detectNbodies     = EditorGUILayout.Toggle(new GUIContent("Automatically Add NBody objects", autoAddTip), gravityEngine.detectNbodies);
            evolveAtStart     = EditorGUILayout.Toggle(new GUIContent("Evolve at Start", autoStartTip), gravityEngine.evolveAtStart);
            physToWorldFactor = EditorGUILayout.FloatField(new GUIContent("Physics to World Scale", phyWTip), gravityEngine.physToWorldFactor);

            EditorGUILayout.LabelField("Steps per frame:");
            stepsPerFrame         = EditorGUILayout.IntField(new GUIContent("  Massive Bodies", stepTip), stepsPerFrame);
            particleStepsPerFrame = EditorGUILayout.IntField(new GUIContent("  Particles", pstepTip), particleStepsPerFrame);
        }
        // Switch bodies list on/off based on option
        if (!gravityEngine.detectNbodies)
        {
            // use native Inspector look & feel for bodies object
            EditorGUILayout.LabelField("Control Nbody in following gameObjects (and children)", EditorStyles.boldLabel);
            SerializedProperty bodiesProp = serializedObject.FindProperty("bodies");
            EditorGUI.BeginChangeCheck();
            EditorGUILayout.PropertyField(bodiesProp, true);
            if (EditorGUI.EndChangeCheck())
            {
                serializedObject.ApplyModifiedProperties();
            }
        }
        else
        {
            EditorGUILayout.LabelField("NBody objects will be detected automatically", EditorStyles.boldLabel);
        }
        // Show the CM and the velocity of the CM
        cmFoldout = EditorGUILayout.Foldout(cmFoldout, "Center of Mass Info");
        if (cmFoldout)
        {
            EditorGUILayout.LabelField("Center of Mass:" + gravityEngine.GetWorldCenterOfMass());
            EditorGUILayout.LabelField("CM Velocity:" + gravityEngine.GetWorldCenterOfMassVelocity());
        }


        // Checking the Event type lets us update after Undo and Redo commands.
        // A redo updates _lengthScale but does not run the setter
        if (Event.current.type == EventType.ExecuteCommand &&
            Event.current.commandName == "UndoRedoPerformed")
        {
            // explicitly re-set so setter code will run.
            gravityEngine.lengthScale = lengthScale;
        }

        if (GUI.changed)
        {
            Undo.RecordObject(gravityEngine, "GE Change");
            gravityEngine.mapToScene = mapToScene;

            gravityEngine.timeScale = timeScale;
            gravityEngine.massScale = massScale;
            // This runs a setter in GE that will do a rescale of the scene if necessary
            gravityEngine.lengthScale       = lengthScale;
            gravityEngine.units             = units;
            gravityEngine.physToWorldFactor = physToWorldFactor;
            gravityEngine.optimizeMassless  = optimizeMassless;
            gravityEngine.detectNbodies     = detectNbodies;

            gravityEngine.editorShowTrajectory    = showTrajectory;
            gravityEngine.trajectoryPrediction    = trajectoryPrediction;
            gravityEngine.trajectoryTime          = trajectoryTime;
            gravityEngine.trajectoryCanvas        = trajCanvas;
            gravityEngine.markerParent            = markerParent;
            gravityEngine.trajectoryComputeFactor = computeFactor;

            gravityEngine.algorithm             = algorithm;
            gravityEngine.force                 = force;
            gravityEngine.evolveAtStart         = evolveAtStart;
            gravityEngine.editorShowScale       = scaling;
            gravityEngine.editorShowAdvanced    = showAdvanced;
            gravityEngine.editorCMfoldout       = cmFoldout;
            gravityEngine.stepsPerFrame         = stepsPerFrame;
            gravityEngine.particleStepsPerFrame = particleStepsPerFrame;
            EditorUtility.SetDirty(gravityEngine);
        }
    }
Beispiel #6
0
    public override void OnInspectorGUI()
    {
        GUI.changed = false;

        NBody   nbody       = (NBody)target;
        float   mass        = 0f;
        float   size        = 0.1f;
        bool    autoSize    = true;
        Vector3 velocity    = Vector3.zero;
        Vector3 initialPos  = Vector3.zero;
        bool    rotateFrame = false;

        if (GravityEngine.Instance() == null)
        {
            EditorGUILayout.LabelField("Require a GravityEngine in the scene to", EditorStyles.boldLabel);
            EditorGUILayout.LabelField("display NBody component.", EditorStyles.boldLabel);
        }

        GravityScaler.Units units = GravityEngine.Instance().units;
        string mass_prompt        = string.Format("Mass ({0})", GravityScaler.MassUnits(units));

        mass = EditorGUILayout.DelayedFloatField(new GUIContent(mass_prompt, mTip), (float)nbody.mass);

        // If the velocity is controlled by an EllipseBase, or this NBody is the direct child of
        // BinaryPair or ThreeeBodySolution then don't allowit to be controlled.
        string controlledBy = null;

        if (nbody.transform.gameObject.GetComponent <OrbitEllipse>() != null)
        {
            controlledBy = "Initial position/Velocity is set by ellipse parameters.";
        }
        else if (nbody.transform.gameObject.GetComponent <OrbitHyper>() != null)
        {
            controlledBy = "Initial position/Velocity is set by hyperbola parameters.";
        }
        else if (nbody.transform.parent != null)
        {
            if (nbody.transform.parent.gameObject.GetComponent <BinaryPair>() != null)
            {
                controlledBy = "Initial position/Velocity is set by BinaryPair parent.";
            }
            else if (nbody.transform.parent.gameObject.GetComponent <ThreeBodySolution>() != null)
            {
                controlledBy = "Initial position/Velocity is set by ThreeBodySolution parent.";
            }
        }
        if (controlledBy == null)
        {
            switch (units)
            {
            case GravityScaler.Units.DIMENSIONLESS:
                EditorGUILayout.LabelField("Initial position set via transform");
                velocity   = EditorGUILayout.Vector3Field(new GUIContent("Velocity", velTip), nbody.vel);
                initialPos = nbody.transform.position;
                break;

            default:
                string prompt = string.Format("Initial Pos ({0})", GravityScaler.LengthUnits(units));
                initialPos = EditorGUILayout.Vector3Field(new GUIContent(prompt, iposTip), nbody.initialPos);

                prompt   = string.Format("Velocity ({0})", GravityScaler.VelocityUnits(units));
                velocity = EditorGUILayout.Vector3Field(new GUIContent(prompt, velTip), nbody.vel);
                break;
            }
        }
        else
        {
            EditorGUILayout.LabelField(controlledBy, EditorStyles.boldLabel);
            //EditorGUILayout.LabelField(string.Format("vel= {0:F2} {1:F2} {2:F2}", nbody.vel.x, nbody.vel.y, nbody.vel.z));
        }


        // particle capture size
        EditorGUIUtility.labelWidth = 200f;
        EditorGUIUtility.fieldWidth = 20f;
        rotateFrame = EditorGUILayout.Toggle(new GUIContent("Rotate frame in orbit", rotateTip), nbody.rotateFrame);
        autoSize    = EditorGUILayout.Toggle(new GUIContent("Automatic particle capture size", autoTip), nbody.automaticParticleCapture);
        EditorGUIUtility.labelWidth = 0;
        EditorGUIUtility.fieldWidth = 0;
        if (!autoSize)
        {
            EditorGUIUtility.labelWidth = 200f;
            EditorGUIUtility.fieldWidth = 40f;
            size = EditorGUILayout.FloatField(new GUIContent("Particle capture radius", sizeTip), (float)nbody.size);
            EditorGUIUtility.labelWidth = 0;
            EditorGUIUtility.fieldWidth = 0;
        }
        else
        {
            float detectedSize = nbody.CalculateSize();
            if (detectedSize < 0)
            {
                EditorGUILayout.LabelField("Did not detect a child with a MeshFilter.", EditorStyles.boldLabel);
                EditorGUILayout.LabelField("Using size=" + size);
            }
            else
            {
                EditorGUILayout.LabelField("Particle Capture radius=" + detectedSize);
                size = detectedSize;
            }
        }
        if (mass < 0)
        {
            mass = 0;
        }

        if (nbody.transform.hasChanged)
        {
            // User has dragged the object and the transform has changed, need
            // to change the initial Pos to correspond to this position in the correct units
            if (units != GravityScaler.Units.DIMENSIONLESS)
            {
                initialPos = nbody.transform.position / GravityEngine.Instance().GetLengthScale();
            }
            nbody.initialPos           = initialPos;
            nbody.transform.hasChanged = false;
        }

        if (GUI.changed)
        {
            Undo.RecordObject(nbody, "NBody Change");
            nbody.mass        = FixNaN.FixIfNaN(mass);
            nbody.vel         = FixNaN.FixIfNaN(velocity);
            nbody.size        = size;
            nbody.rotateFrame = rotateFrame;
            nbody.automaticParticleCapture = autoSize;
            nbody.initialPos = initialPos;
            Debug.Log("new v=" + velocity);
            // must be after initialPos is updated
            nbody.ApplyScale(GravityEngine.Instance().GetLengthScale(),
                             GravityEngine.Instance().GetVelocityScale());
            EditorUtility.SetDirty(nbody);
        }
    }
    public override void OnInspectorGUI()
    {
        GUI.changed = false;
        EllipseBase ellipseBase = (EllipseBase)target;
        // fields in class
        GameObject centerObject = null;

        EllipseBase.ParamBy paramBy = EllipseBase.ParamBy.AXIS_A;
        float ecc         = 0;
        float a           = 0;
        float p           = 0;
        float omega_uc    = 0;
        float omega_lc    = 0;
        float inclination = 0;
        float phase       = 0;

        if (!(target is BinaryPair))
        {
            centerObject = (GameObject)EditorGUILayout.ObjectField(
                new GUIContent("CenterObject", centerTip),
                ellipseBase.centerObject,
                typeof(GameObject),
                true);
        }

        EditorGUILayout.Space();
        EditorGUILayout.LabelField("Ellipse Parameters", EditorStyles.boldLabel);

        // If there is a SolarBody, it is the one place data can be changed. The EB holds the
        // orbit scaled per SolarSystem scale.
        SolarBody sbody = ellipseBase.GetComponent <SolarBody>();

        if (sbody != null)
        {
            EditorGUILayout.LabelField("\tEllipse parameters controlled by SolarBody settings.");
            EditorGUILayout.LabelField(string.Format("   {0,-25}\t ({1,1})\t  {2}",
                                                     "Semi-Major Axis", "a", ellipseBase.a), EditorStyles.wordWrappedLabel);
            EditorGUILayout.LabelField(string.Format("   {0,-25}\t ({1,1})\t  {2}",
                                                     "Eccentricity", "e", ellipseBase.ecc), EditorStyles.wordWrappedLabel);
            EditorGUILayout.LabelField(string.Format("   {0,-25}\t ({1,1})\t  {2}",
                                                     "Incliniation", "i", ellipseBase.inclination), EditorStyles.wordWrappedLabel);
            EditorGUILayout.LabelField(string.Format("   {0,-25}\t ({1,1})\t  {2}",
                                                     "Arg. of pericenter", "\u03c9", ellipseBase.omega_lc), EditorStyles.wordWrappedLabel);
            EditorGUILayout.LabelField(string.Format("   {0,-25}\t ({1,1})\t  {2}",
                                                     "Longitude of node", "\u03a9", ellipseBase.omega_uc), EditorStyles.wordWrappedLabel);
            EditorGUILayout.LabelField(string.Format("   {0,-25}\t ({1,1})\t  {2}",
                                                     "Phase", "M", ellipseBase.phase), EditorStyles.wordWrappedLabel);
            return;
        }

        paramBy =
            (EllipseBase.ParamBy)EditorGUILayout.EnumPopup(new GUIContent("Parameter Choice", paramTip), ellipseBase.paramBy);

        ecc = EditorGUILayout.Slider(new GUIContent("Eccentricity", eTip), ellipseBase.ecc, 0f, 0.99f);


        axisUpdated = false;
        // backwards compatibility when loading scenes before unit scaling:
        if (ellipseBase.a_scaled < 0)
        {
            axisUpdated = true;
        }
        GravityScaler.Units units = GravityEngine.Instance().units;
        if (ellipseBase.paramBy == EllipseBase.ParamBy.AXIS_A)
        {
            float oldLabelWidth = EditorGUIUtility.labelWidth;
            EditorGUIUtility.labelWidth = oldLabelWidth + 30f;
            string prompt = string.Format("Semi-Major Axis (a) [{0}]", GravityScaler.LengthUnits(units));
            a = EditorGUILayout.DelayedFloatField(new GUIContent(prompt, aTip), ellipseBase.a);
            if (a != ellipseBase.a)
            {
                axisUpdated = true;
            }
            EditorGUILayout.LabelField("Scaled a (Unity units):   " + ellipseBase.a_scaled);
            EditorGUIUtility.labelWidth = oldLabelWidth;
            p = a * (1 - ecc);
        }
        else
        {
            string prompt = string.Format("Pericenter [{0}]", GravityScaler.LengthUnits(units));
            p = EditorGUILayout.DelayedFloatField(new GUIContent(prompt, pTip), ellipseBase.p);
            if (p != ellipseBase.p)
            {
                axisUpdated = true;
            }
            EditorGUILayout.LabelField("Scaled p (Unity units) " + ellipseBase.p_scaled);
            a = p / (1 - ecc);
        }
        // implementation uses AngleAxis, so degrees are more natural
        omega_uc    = EditorGUILayout.Slider(new GUIContent("\u03a9 (Longitude of AN)", omega_ucTip), ellipseBase.omega_uc, 0, 360f);
        omega_lc    = EditorGUILayout.Slider(new GUIContent("\u03c9 (AN to Pericenter)", omega_lcTip), ellipseBase.omega_lc, 0, 360f);
        inclination = EditorGUILayout.Slider(new GUIContent("Inclination", inclinationTip), ellipseBase.inclination, 0f, 180f);
        // physics uses radians - but ask user for degrees to be consistent
        phase = EditorGUILayout.Slider(new GUIContent("Starting Phase", phaseTip), ellipseBase.phase, 0, 360f);

        // Checking the Event type lets us update after Undo and Redo commands.
        // A redo updates _lengthScale but does not run the setter
//        if (Event.current.type == EventType.ExecuteCommand &&
//            Event.current.commandName == "UndoRedoPerformed") {
//			ellipseBase.ApplyScale(GravityEngine.Instance().GetLengthScale());
//        }

        if (GUI.changed)
        {
            Undo.RecordObject(ellipseBase, "EllipseBase Change");
            ellipseBase.a            = a;
            ellipseBase.p            = p;
            ellipseBase.ecc          = ecc;
            ellipseBase.centerObject = centerObject;
            ellipseBase.omega_lc     = omega_lc;
            ellipseBase.omega_uc     = omega_uc;
            ellipseBase.inclination  = inclination;
            ellipseBase.phase        = phase;
            ellipseBase.paramBy      = paramBy;
            EditorUtility.SetDirty(ellipseBase);
        }
    }
    public override void OnInspectorGUI()
    {
        GUI.changed = false;
        OrbitUniversal orbitU         = (OrbitUniversal)target;
        bool           displayAndExit = false;

        // check there is an NBody, if not likely a synthesized Orbit as part of a predictor
        if (orbitU.GetComponent <NBody>() == null)
        {
            EditorGUILayout.LabelField("Ellipse parameters determined from position/velocity.");
            EditorGUILayout.LabelField("(Orbit Predictor).");
            displayAndExit = true;
        }

        // If there is a SolarBody, it is the one place data can be changed. The EB holds the
        // orbit scaled per SolarSystem scale.
        SolarBody sbody = orbitU.GetComponent <SolarBody>();

        if (sbody != null)
        {
            EditorGUILayout.LabelField("Ellipse parameters controlled by SolarBody settings.");
            displayAndExit = true;
        }
        if (displayAndExit)
        {
            EditorGUILayout.LabelField(string.Format("   {0,-25} ({1,1})\t  {2}",
                                                     "Semi-Major Axis", "a", orbitU.GetMajorAxisInspector()), EditorStyles.wordWrappedLabel);
            EditorGUILayout.LabelField(string.Format("   {0,-25} ({1,1})\t  {2}",
                                                     "Eccentricity", "e", orbitU.eccentricity), EditorStyles.wordWrappedLabel);
            EditorGUILayout.LabelField(string.Format("   {0,-25} ({1,1})\t  {2}",
                                                     "Incliniation", "i", orbitU.inclination), EditorStyles.wordWrappedLabel);
            EditorGUILayout.LabelField(string.Format("   {0,-25} ({1,1})\t  {2}",
                                                     "Arg. of pericenter", "\u03c9", orbitU.omega_lc), EditorStyles.wordWrappedLabel);
            EditorGUILayout.LabelField(string.Format("   {0,-25} ({1,1})\t  {2}",
                                                     "Longitude of node", "\u03a9", orbitU.omega_uc), EditorStyles.wordWrappedLabel);
            EditorGUILayout.LabelField(string.Format("   {0,-25} ({1,1})\t  {2}",
                                                     "Phase", "M", orbitU.phase), EditorStyles.wordWrappedLabel);
            return;
        }

        // fields in class
        NBody centerNBody = null;

        OrbitUniversal.InputMode inputMode = orbitU.inputMode;
        double ecc         = orbitU.eccentricity;
        double p_inspector = orbitU.p_inspector;
        double omega_uc    = 0;
        double omega_lc    = 0;
        double inclination = 0;
        double phase       = 0;

        bool sizeUpdate = false;

        WarnAboutKeplerSeq(orbitU);

        centerNBody = (NBody)EditorGUILayout.ObjectField(
            new GUIContent("Center NBody", centerTip),
            orbitU.centerNbody,
            typeof(NBody),
            true);


        OrbitUniversal.EvolveMode evolveMode = orbitU.evolveMode;
        evolveMode = (OrbitUniversal.EvolveMode)EditorGUILayout.EnumPopup(new GUIContent("Evolve Mode", modeTip), evolveMode);

        EditorGUILayout.Space();
        EditorGUILayout.LabelField("Shape Parameters", EditorStyles.boldLabel);


        inputMode = (OrbitUniversal.InputMode)
                    EditorGUILayout.EnumPopup(new GUIContent("Parameter Choice", paramTip), orbitU.inputMode);

        sizeUpdate = false;
        GravityScaler.Units units = GravityEngine.Instance().units;
        string promptp            = string.Format("Semi-parameter (p) [{0}]", GravityScaler.LengthUnits(units));

        // The values for orbit size and shape can be entered in several ways. OrbitUniversal supports
        // these values as doubles (which is probably overkill in most case). Provide an explicit double mode
        // but also allow sliders (which reduce the value to a float).

        // The editor script changes p_inspector. (p in OU is scaled for GE internal units)
        switch (inputMode)
        {
        case OrbitUniversal.InputMode.ELLIPSE_MAJOR_AXIS_A:
            EditorGUILayout.LabelField("Ellipse with float/sliders using semi-major axis.");
            ecc = EditorGUILayout.Slider(new GUIContent("Eccentricity", eTip), (float)orbitU.eccentricity, 0f, 0.99f);
            GetMajorAxis(orbitU, ref p_inspector, ref sizeUpdate, units);
            break;

        case OrbitUniversal.InputMode.ELLIPSE_APOGEE_PERIGEE:
            EditorGUILayout.LabelField("Ellipse with float/sliders using agogee/perigee.");
            EditorGUILayout.LabelField("MUST use <Return> after updating values!");
            double apogee_old  = orbitU.GetApogeeInspector();
            double apogee      = EditorGUILayout.DelayedDoubleField(new GUIContent("Apogee", eTip), apogee_old);
            double perigee_old = orbitU.GetPerigeeInspector();
            double perigee     = EditorGUILayout.DelayedDoubleField(new GUIContent("Perigee", eTip), perigee_old);
            // enforce apogee > perigee
            if (apogee < perigee)
            {
                apogee = perigee;
            }

            if (!EditorApplication.isPlaying && (apogee != apogee_old) || (perigee != perigee_old))
            {
                orbitU.SetSizeWithApogeePerigee(apogee, perigee);
                sizeUpdate = true;
                // Need to update ecc and p with new values
                p_inspector = orbitU.p_inspector;
                ecc         = orbitU.eccentricity;
            }
            EditorGUILayout.LabelField(string.Format("Require Apogee > Perigee", ecc, p_inspector));
            EditorGUILayout.LabelField(string.Format("Apogee/Perigee result in: eccentricty={0:0.00}, p={1:0.00}", ecc, p_inspector));
            break;

        case OrbitUniversal.InputMode.ECC_PERIGEE:
            EditorGUILayout.LabelField("Orbit with double using eccentricity/perigee.");
            EditorGUILayout.LabelField("MUST use <Return> after updating values!");
            double old_ecc = orbitU.eccentricity;
            ecc = EditorGUILayout.DelayedDoubleField(new GUIContent("Eccentricity", eTip), orbitU.eccentricity);
            double hperigee_old = orbitU.GetPerigeeInspector();
            double hperigee     = EditorGUILayout.DelayedDoubleField(new GUIContent("Perigee", eTip), hperigee_old);
            if (!EditorApplication.isPlaying && ((hperigee != hperigee_old) || (old_ecc != ecc)))
            {
                orbitU.SetSizeWithEccPerigee(ecc, hperigee);
                sizeUpdate = true;
                // Need to update ecc and p with new values
                p_inspector = orbitU.p_inspector;
                ecc         = orbitU.eccentricity;
            }
            EditorGUILayout.LabelField(string.Format("Apogee/Perigee result in: eccentricty={0:0.00}, p={1:0.00}", ecc, p_inspector));
            break;


        case OrbitUniversal.InputMode.DOUBLE:
            EditorGUILayout.LabelField("Specify values with double precision using semi-parameter");
            EditorGUILayout.LabelField("MUST use <Return> after updating values!");
            // no sliders (they do float)
            ecc = EditorGUILayout.DelayedDoubleField(new GUIContent("Eccentricity", eTip), orbitU.eccentricity);
            double old_p = orbitU.p_inspector;
            p_inspector = EditorGUILayout.DelayedDoubleField(new GUIContent(promptp, pTip), orbitU.p_inspector);
            if (old_p != p_inspector)
            {
                sizeUpdate = true;
            }
            break;

        case OrbitUniversal.InputMode.DOUBLE_ELLIPSE:
            EditorGUILayout.LabelField("Specify values with double precision using semi-parameter");
            EditorGUILayout.LabelField("MUST use <Return> after updating values!");
            // no sliders (they do float)
            ecc = EditorGUILayout.DelayedDoubleField(new GUIContent("Eccentricity", eTip), orbitU.eccentricity);
            GetMajorAxis(orbitU, ref p_inspector, ref sizeUpdate, units);
            break;

        default:
            Debug.LogWarning("Unknown input mode - internal error");
            break;
        }
        if (!EditorApplication.isPlaying && (p_inspector != orbitU.p))
        {
            sizeUpdate = true;
        }
        EditorGUILayout.LabelField("Scaled p (Unity units):   " + orbitU.p);

        EditorGUILayout.Space();
        EditorGUILayout.LabelField("Orientation Parameters", EditorStyles.boldLabel);
        if ((inputMode != OrbitUniversal.InputMode.DOUBLE) && (inputMode != OrbitUniversal.InputMode.DOUBLE_ELLIPSE))
        {
            // implementation uses AngleAxis, so degrees are more natural
            omega_uc    = EditorGUILayout.Slider(new GUIContent("\u03a9 (Longitude of AN)", omega_ucTip), (float)orbitU.omega_uc, 0, 360f);
            omega_lc    = EditorGUILayout.Slider(new GUIContent("\u03c9 (AN to Pericenter)", omega_lcTip), (float)orbitU.omega_lc, 0, 360f);
            inclination = EditorGUILayout.Slider(new GUIContent("Inclination", inclinationTip), (float)orbitU.inclination, 0f, 180f);
            // physics uses radians - but ask user for degrees to be consistent
            phase = EditorGUILayout.Slider(new GUIContent("Starting Phase", phaseTip), (float)orbitU.phase, 0, 360f);
        }
        else
        {
            // DOUBLE, so no sliders
            omega_uc    = EditorGUILayout.DoubleField(new GUIContent("\u03a9 (Longitude of AN)", omega_ucTip), orbitU.omega_uc);
            omega_lc    = EditorGUILayout.DoubleField(new GUIContent("\u03c9 (AN to Pericenter)", omega_lcTip), orbitU.omega_lc);
            inclination = EditorGUILayout.DoubleField(new GUIContent("Inclination", inclinationTip), orbitU.inclination);
            phase       = EditorGUILayout.DoubleField(new GUIContent("Starting Phase", phaseTip), orbitU.phase);
        }

        if (GUI.changed)
        {
            Undo.RecordObject(orbitU, "EllipseBase Change");
            orbitU.p_inspector  = p_inspector;
            orbitU.eccentricity = ecc;
            orbitU.centerNbody  = centerNBody;
            orbitU.omega_lc     = omega_lc;
            orbitU.omega_uc     = omega_uc;
            orbitU.inclination  = inclination;
            orbitU.phase        = phase;
            orbitU.inputMode    = inputMode;
            orbitU.evolveMode   = evolveMode;
            EditorUtility.SetDirty(orbitU);
        }

        if (sizeUpdate)
        {
            orbitU.ApplyScale(GravityEngine.Instance().GetLengthScale());
        }
    }
    private static void GetMajorAxis(OrbitUniversal orbitU, ref double p_inspector, ref bool sizeUpdate, GravityScaler.Units units)
    {
        float oldLabelWidth = EditorGUIUtility.labelWidth;

        EditorGUIUtility.labelWidth = oldLabelWidth + 50f;
        string prompt = string.Format("Semi-Major Axis (a) [{0}]", GravityScaler.LengthUnits(units));
        double old_a  = orbitU.GetMajorAxisInspector();
        double a      = EditorGUILayout.DelayedDoubleField(new GUIContent(prompt, aTip), old_a);

        if (!EditorApplication.isPlaying && (a != old_a))
        {
            orbitU.SetMajorAxisInspector(a);
            sizeUpdate = true;
            // Need to update ecc and p with new values
            p_inspector = orbitU.p_inspector;
        }
        EditorGUILayout.LabelField(string.Format("Axis result in:  p={0:0.00}", p_inspector));
        EditorGUIUtility.labelWidth = oldLabelWidth;
    }