public ThrustContribution(Thruster thruster, int index, Vector3D translationForce, Vector3D torque)
            {
                this.Thruster = thruster;
                this.Index = index;

                this.TranslationForceLength = translationForce.Length;
                this.TranslationForce = translationForce;
                this.TranslationForceUnit = new Vector3D(translationForce.X / this.TranslationForceLength, translationForce.Y / this.TranslationForceLength, translationForce.Z / this.TranslationForceLength);

                this.TorqueLength = torque.Length;
                this.Torque = torque;
                this.TorqueUnit = new Vector3D(torque.X / this.TorqueLength, torque.Y / this.TorqueLength, torque.Z / this.TorqueLength);
            }
 public ThrusterSetting(Thruster thruster, int thrusterIndex, int subIndex, double percent)
 {
     this.Thruster = thruster;
     this.ThrusterIndex = thrusterIndex;
     this.SubIndex = subIndex;
     this.Percent = percent;
 }
        public ThrusterMap(IEnumerable<ThrusterSetting> thrusterUsage, Thruster[] allThrusters, long botToken)
        {
            #region build flattened

            var flattened = new List<Tuple<int, int, double>>();

            for (int outer = 0; outer < allThrusters.Length; outer++)
            {
                ThrusterSetting[] matches = thrusterUsage.
                    Where(o => o.ThrusterIndex == outer).
                    ToArray();

                for (int inner = 0; inner < allThrusters[outer].ThrusterDirectionsModel.Length; inner++)
                {
                    ThrusterSetting match = matches.FirstOrDefault(o => o.SubIndex == inner);
                    double percent = match == null ? 0d : match.Percent;
                    flattened.Add(Tuple.Create(outer, inner, percent));
                }
            }

            #endregion

            this.UsedThrusters = thrusterUsage.ToArray();
            this.Flattened = flattened.ToArray();
            this.AllThrusters = allThrusters;
            this.BotToken = botToken;
        }
        public ThrusterMap(Tuple<int, int, double>[] flattened, Thruster[] allThrusters, long botToken)
        {
            #region build used

            List<ThrusterSetting> used = new List<ThrusterSetting>();

            foreach (var item in flattened)
            {
                if (Math1D.IsNearZero(item.Item3))
                {
                    continue;
                }

                used.Add(new ThrusterSetting(allThrusters[item.Item1], item.Item1, item.Item2, item.Item3));
            }

            #endregion

            this.UsedThrusters = used.ToArray();
            this.Flattened = flattened;
            this.AllThrusters = allThrusters;
            this.BotToken = botToken;
        }
        private static Tuple<int, int, ThrustContribution>[] GetThrusterContributions(Thruster[] thrusters, bool[] isDestroyed, Point3D centerOfMass)
        {
            //This method is copied from ShipPartTesterWindow (and ShipPlayer)

            var retVal = new List<Tuple<int, int, ThrustContribution>>();

            for (int outer = 0; outer < thrusters.Length; outer++)
            {
                if(isDestroyed[outer])
                {
                    // This thruster is destroyed, so it has no contribution (leaving it in the list of contributions so that thruster maps stay the same size as
                    // thrusters get destroyed and repaired
                    retVal.AddRange(
                        Enumerable.Range(0, thrusters[outer].ThrusterDirectionsShip.Length).
                            Select(o => Tuple.Create(outer, o, new ThrustContribution(thrusters[outer], o, true, new Vector3D(0, 0, 0), new Vector3D(0, 0, 0))))
                        );
                    continue;
                }

                for (int inner = 0; inner < thrusters[outer].ThrusterDirectionsShip.Length; inner++)
                {
                    // This is copied from Body.AddForceAtPoint

                    Vector3D offsetFromMass = thrusters[outer].Position - centerOfMass;		// this is ship's local coords
                    Vector3D force = thrusters[outer].ThrusterDirectionsShip[inner] * thrusters[outer].ForceAtMax;

                    Vector3D translationForce, torque;
                    Math3D.SplitForceIntoTranslationAndTorque(out translationForce, out torque, offsetFromMass, force);

                    retVal.Add(Tuple.Create(outer, inner, new ThrustContribution(thrusters[outer], inner, false, translationForce, torque)));
                }
            }

            return retVal.ToArray();
        }
        //TODO: Take in the mass matrix - this isn't necessary, but helps with fine tuning
        public ThrustContributionModel(Thruster[] thrusters, Point3D centerOfMass)
        {
            this.IsDestroyed = thrusters.       // caching this in case the property changes mid calculations
                Select(o => o.IsDestroyed).
                ToArray();

            this.CenterOfMass = centerOfMass;

            this.Contributions = GetThrusterContributions(thrusters, this.IsDestroyed, centerOfMass);
        }
        //TODO: Come up with some overloads that have various constraints (only thrusters that contribute to a direction, x percent of the thrusters, etc)
        public static ThrusterMap GenerateRandomMap(Thruster[] allThrusters, long botToken)
        {
            Random rand = StaticRandom.GetRandomForThread();

            var flattened = new List<Tuple<int, int, double>>();

            for (int outer = 0; outer < allThrusters.Length; outer++)
            {
                for (int inner = 0; inner < allThrusters[outer].ThrusterDirectionsModel.Length; inner++)
                {
                    flattened.Add(Tuple.Create(outer, inner, rand.NextDouble()));
                }
            }

            return new ThrusterMap(Normalize(flattened.ToArray()), allThrusters, botToken);
        }
        private void btnStandaloneThruster_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                ThrusterDNA dna = new ThrusterDNA()
                {
                    PartType = Thruster.PARTTYPE,
                    ThrusterType = UtilityCore.GetRandomEnum<ThrusterType>(ThrusterType.Custom),
                    Position = new Point3D(0, 0, 0),
                    Orientation = Quaternion.Identity,
                    Scale = new Vector3D(1, 1, 1)
                };
                ModifyDNA(dna, chkStandaloneRandSize.IsChecked.Value, chkStandaloneRandOrientation.IsChecked.Value);
                double size = (dna.Scale.X + dna.Scale.Y + dna.Scale.Z) / 3d;
                dna.Scale = new Vector3D(size, size, size);

                Thruster thruster = new Thruster(_editorOptions, _itemOptions, dna, null);

                BuildStandalonePart(thruster);

                if (chkStandaloneShowMassBreakdown.IsChecked.Value)
                {
                    double cellSize = Math1D.Max(dna.Scale.X, dna.Scale.Y, dna.Scale.Z) * UtilityCore.GetScaledValue_Capped(.1d, .3d, 0d, 1d, _rand.NextDouble());
                    DrawMassBreakdown(thruster.GetMassBreakdown(cellSize), cellSize);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        private void btnThruster_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                ShipPartDNA dna = GetDefaultDNA(FuelTank.PARTTYPE);
                FuelTank fuelTank = new FuelTank(_editorOptions, _itemOptions, dna);

                ThrusterDNA dna2 = new ThrusterDNA()
                {
                    PartType = Thruster.PARTTYPE,
                    ThrusterType = UtilityCore.GetRandomEnum<ThrusterType>(),
                    Position = new Point3D(0, 0, 0),
                    Orientation = Quaternion.Identity,
                    Scale = new Vector3D(1, 1, 1)
                };
                Thruster thruster = new Thruster(_editorOptions, _itemOptions, dna2, fuelTank);

                fuelTank.QuantityCurrent = fuelTank.QuantityMax;

                double percent;
                Vector3D? thrust;

                do
                {
                    percent = 1d;
                    thrust = thruster.Fire(ref percent, 0, 1d);
                } while (fuelTank.QuantityCurrent > 0d);

                percent = 1d;
                thrust = thruster.Fire(ref percent, 0, 1d);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
                public ThrustSetting(Thruster thruster, int index, Vector3D translationForceFull, Vector3D torqueFull, Vector3D translationForceUnit, Vector3D torqueUnit, Vector3D translationForce, Vector3D torque, double percent)
                {
                    this.Thruster = thruster;
                    this.Index = index;

                    this.TranslationForceFull = translationForceFull;
                    this.TorqueFull = torqueFull;

                    this.TranslationForceUnit = translationForceUnit;
                    this.TorqueUnit = torqueUnit;

                    this.TranslationForce = translationForce;
                    this.Torque = torque;

                    this.Percent = percent;
                }
                public ThrustContribution(Thruster thruster, int index, Vector3D translationForce, Vector3D torque)
                {
                    this.Thruster = thruster;
                    this.Index = index;
                    this.TranslationForce = translationForce;
                    this.Torque = torque;

                    //this.TranslationLength = translationForce.Length;
                    //this.TorqueLength = torque.Length;
                }
 public ThrusterSetting(Thruster thruster, int index, double percent)
 {
     this.Thruster = thruster;
     this.Index = index;
     this.Percent = percent;
 }