Example #1
0
        private static Model3D GetAxeCylinder(double radius, double height, double scale, double yOffset, MaterialGroup material)
        {
            GeometryModel3D retVal = new GeometryModel3D();

            retVal.Material     = material;
            retVal.BackMaterial = material;

            double bevel = radius * .2;

            List <TubeRingBase> tubes = new List <TubeRingBase>();

            tubes.Add(new TubeRingRegularPolygon(0, false, radius - bevel, radius - bevel, true));
            tubes.Add(new TubeRingRegularPolygon(bevel, false, radius, radius, false));
            tubes.Add(new TubeRingRegularPolygon(height - (bevel * 2), false, radius, radius, false));
            tubes.Add(new TubeRingRegularPolygon(bevel, false, radius - bevel, radius - bevel, true));

            retVal.Geometry = UtilityWPF.GetMultiRingedTube(10, tubes, true, true);

            Transform3DGroup transform = new Transform3DGroup();

            transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), 90)));      // the tube is built along z, rotate so it's along y

            if (!Math1D.IsNearZero(yOffset))
            {
                transform.Children.Add(new TranslateTransform3D(0, yOffset, 0));
            }

            transform.Children.Add(new ScaleTransform3D(scale, scale, scale));

            retVal.Transform = transform;

            return(retVal);
        }
Example #2
0
        private static Model3D GetAxeSpike(double radius, double length, double scale, double yOffset, MaterialGroup materialMiddle, MaterialGroup materialEdge)
        {
            GeometryModel3D retVal = new GeometryModel3D();

            retVal.Material     = materialEdge;
            retVal.BackMaterial = materialEdge;

            double bevel = radius * .2;

            List <TubeRingBase> tubes = new List <TubeRingBase>();

            tubes.Add(new TubeRingRegularPolygon(0, false, radius, radius * 2, true));
            tubes.Add(new TubeRingPoint(length, false));

            retVal.Geometry = UtilityWPF.GetMultiRingedTube(10, tubes, true, false);

            Transform3DGroup transform = new Transform3DGroup();

            transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), -90)));      // the tube is built along z, rotate so it's along x

            if (!Math1D.IsNearZero(yOffset))
            {
                transform.Children.Add(new TranslateTransform3D(0, yOffset, 0));
            }

            transform.Children.Add(new ScaleTransform3D(scale, scale, scale));

            retVal.Transform = transform;

            return(retVal);
        }
Example #3
0
        private void TransitionToFinal()
        {
            List <TrackingCandidate> finalists = new List <TrackingCandidate>();

            #region Find finished finalists

            // Get all the finalists that are finished
            int index = 0;
            while (index < _finalists.Count)
            {
                TrackingCandidate finalist = _finalists[index];
                int numStarted             = finalist.NumStarted;

                if (numStarted >= this.FinalistCount && numStarted == finalist.NumFinished)
                {
                    if (!Math1D.IsNearZero(finalist.GetAverage()))              // don't let zero scores into the final (the way to get a zero score is to become a candidate, but then each attempt resulted in a zero score)
                    {
                        finalists.Add(finalist);
                    }

                    _finalists.RemoveAt(index);
                }
                else
                {
                    index++;
                }
            }

            #endregion

            if (finalists.Count > 0)
            {
                this.Final.StoreWinners(finalists.Select(o => new WinnerList.WinningBean(o.DNA, o.GetAverage(), 0d)).ToArray());
            }
        }
Example #4
0
        public DamageProps CalculateDamage(MaterialCollision[] collisions)
        {
            if (!_isFullySetUp || collisions.Length == 0)
            {
                return(null);
            }

            double damangeMult = GetDamageMultiplier();

            if (Math1D.IsNearZero(damangeMult))
            {
                return(null);
            }

            var avgCollision = MaterialCollision.GetAverageCollision(collisions, _bot.PhysicsBody);

            //TODO: See if this position is along the ramming direction

            double speed = avgCollision.Item2 / 10;     // a speed of 10 is considered an average impact speed

            double damage = speed * damangeMult;

            DamageProps retVal = new DamageProps(avgCollision.Item1, damage);

            // The act of hitting something needs to stop the ram, otherwise, they just keep pushing against the item
            // and continue to do damage
            StopRamming();

            return(retVal);
        }
Example #5
0
        private bool AreForcesZero()
        {
            if (this.Field == null || _triangles == null || _triangles.Length == 0)
            {
                return(true);
            }

            bool isZeroForce = false;

            if (this.Field is FluidFieldUniform)
            {
                var velocities = this.Field.GetForce(new[] { new Point3D(0, 0, 0) }, 1);

                isZeroForce = Math1D.IsNearZero(velocities[0].Item1.LengthSquared);
            }

            if (isZeroForce && (this.Body == null || (Math1D.IsNearZero(this.Body.Velocity.LengthSquared) && Math1D.IsNearZero(this.Body.AngularVelocity.LengthSquared))))
            {
                return(true);
            }
            else
            {
                return(false);
            }
        }
Example #6
0
        private Vector3D AvoidWorker()
        {
            Vector3D retVal = new Vector3D(0, 0, 0);

            Vector3D myPositionWorld = this.PhysicsBody.PositionToWorld(this.PhysicsBody.CenterOfMass).ToVector();       // center mass should always be at zero, but I want to be consistent

            foreach (SwarmBot1 bot in _otherBots)
            {
                // Get its position relative to me
                Vector3D botPositionWorld = bot.PhysicsBody.PositionToWorld(bot.PhysicsBody.CenterOfMass).ToVector();

                Vector3D offsetLocal = this.PhysicsBody.DirectionFromWorld(botPositionWorld - myPositionWorld);

                // I need to invert this, because the greater the distance, the less the influence
                double offsetLength = offsetLocal.Length;
                if (Math1D.IsNearZero(offsetLength))
                {
                    // Can't divide by zero.  For now, I'll just skip this bot
                    continue;
                }

                double awayForce = 1 / offsetLength;

                offsetLocal /= offsetLength;  // normalize
                offsetLocal *= -awayForce;    // point away from the bot, with the away force strength

                // Now add this to the return vector
                retVal += offsetLocal;
            }

            // Exit Function
            return(retVal);
        }
Example #7
0
        /// <summary>
        /// a1 is line1 start, a2 is line1 end, b1 is line2 start, b2 is line2 end
        /// </summary>
        /// <remarks>
        /// Got this here:
        /// http://stackoverflow.com/questions/14480124/how-do-i-detect-triangle-and-rectangle-intersection
        ///
        /// A similar article:
        /// http://www.flipcode.com/archives/Point-Plane_Collision.shtml
        /// </remarks>
        private static Vector?Intersects(Vector a1, Vector a2, Vector b1, Vector b2)
        {
            Vector b         = Vector.Subtract(a2, a1);
            Vector d         = Vector.Subtract(b2, b1);
            double bDotDPerp = (b.X * d.Y) - (b.Y * d.X);

            // if b dot d == 0, it means the lines are parallel so have infinite intersection points
            if (Math1D.IsNearZero(bDotDPerp))
            {
                return(null);
            }

            Vector c = Vector.Subtract(b1, a1);
            double t = (c.X * d.Y - c.Y * d.X) / bDotDPerp;

            if (t < 0 || t > 1)
            {
                return(null);
            }

            double u = (c.X * b.Y - c.Y * b.X) / bDotDPerp;

            if (u < 0 || u > 1)
            {
                return(null);
            }

            // Return the intersection point
            return(Vector.Add(a1, Vector.Multiply(b, t)));
        }
Example #8
0
        private void PhysicsBody_BodyMoved(object sender, EventArgs e)
        {
            if (!_isVisualAdded)
            {
                return;
            }

            // Graphics need to be updated here.  If I wait until world update, this graphic will be one frame
            // behind, which is very noticable when the bot has a high velocity

            double damageMult = GetDamageMultiplier();

            Vector3D velocity = _bot.VelocityWorld;
            var      dna      = this.DNA;

            // Light size
            double lightSize = UtilityCore.GetScaledValue_Capped(0, _bot.Radius * 4, 0, dna.DamageMult * 20, damageMult);

            UtilityWPF.SetAttenuation(_light, lightSize, .1d);
            _light.Color = Color.FromArgb(Convert.ToByte(UtilityCore.GetScaledValue_Capped(0, 192, 0, dna.DamageMult * 10, damageMult)), 255, 0, 0);

            // Scale
            double scale;

            if (damageMult < dna.DamageMult * 4)
            {
                scale = 0;
            }
            else
            {
                scale = UtilityCore.GetScaledValue_Capped(0, _bot.Radius, dna.DamageMult * 4, dna.DamageMult * 25, damageMult);
            }
            _scale.ScaleX = scale;
            _scale.ScaleY = scale;
            _scale.ScaleZ = scale;

            // Translate
            Point3D position = _bot.PositionWorld + (velocity.ToUnit() * (_bot.Radius * UtilityCore.GetScaledValue_Capped(1.05, 1.3, 0, dna.DamageMult * 25, damageMult)));

            _translate.OffsetX = position.X;
            _translate.OffsetY = position.Y;
            _translate.OffsetZ = position.Z;

            // Rotate
            Vector3D xAxis = new Vector3D(1, 0, 0);

            Vector3D axis  = Vector3D.CrossProduct(xAxis, velocity);
            double   angle = Vector3D.AngleBetween(xAxis, velocity);

            if (Math3D.IsNearZero(axis) || Math1D.IsNearZero(angle))
            {
                _rotate.Quaternion = Quaternion.Identity;
            }
            else
            {
                _rotate.Quaternion = new Quaternion(axis, angle);
            }
        }
Example #9
0
        private void Brain_StraightToTarget_VelocityAware2()
        {
            // Velocity should be zero when touching the chase point

            //TODO:  Implement this (when not attacking, velocity should slow to a max speed the closer to the chase point)



            // Convert everything to local coords
            Point3D position   = new Point3D(0, 0, 0);
            Point3D chasePoint = this.PhysicsBody.PositionFromWorld(_chasePoint);

            Vector3D directionToGo = chasePoint.ToVector() - position.ToVector();

            Quaternion rotation;

            #region Adjust for current velocity attempt1a

            Vector3D currentVelocity = this.VelocityWorld;

            if (!Math1D.IsNearZero(currentVelocity.LengthSquared))
            {
                currentVelocity = this.PhysicsBody.DirectionFromWorld(currentVelocity);

                rotation = Math3D.GetRotation(directionToGo, currentVelocity);

                // This is how much to rotate direction to align with current velocity, I want to go against the current velocity (if aligned,
                // the angle will be zero, so negating won't make a difference)
                rotation = rotation.ToReverse();

                // If it's greater than 90 degrees, then just use the original direction (because it will pull the velocity in line
                // eventually)  I don't multiply by .5, because when it is very close to 90 degrees, the bot will thrash a bit
                if (Math.Abs(Math1D.DegreesToRadians(rotation.Angle)) < Math.PI * .4d)
                {
                    // Change the direction by the angle
                    directionToGo = rotation.GetRotatedVector(directionToGo);
                }
            }

            #endregion

            // Now that I know where to go, rotate the original thruster direction (0,1,0) to line up with the desired direction
            rotation = Math3D.GetRotation(_origThrustDirection, directionToGo);

            // Thrust Direction
            _thrustTransform = new RotateTransform3D(new QuaternionRotation3D(rotation));

            // Thrust Strength
            if (_isAttacking)
            {
                _thrustPercent = 1d;
            }
            else
            {
                _thrustPercent = .5d;
            }
        }
Example #10
0
        private static RayHitTestParameters CastRay_PlaneLimited_GetSnapLine(ITriangle plane, Vector3D lookDirection)
        {
            const double THRESHOLD = .33d;

            #region Get orthogonal vectors

            Point3D      centerPoint = new Point3D();
            DoubleVector testVectors = new DoubleVector();

            //This assumes that the plane was set up with orthogonal vectors, and that these are the ones to use in this case

            for (int cntr = 0; cntr < 3; cntr++)
            {
                switch (cntr)
                {
                case 0:
                    centerPoint = plane.Point0;
                    testVectors = new DoubleVector((plane.Point1 - plane.Point0).ToUnit(), (plane.Point2 - plane.Point0).ToUnit());
                    break;

                case 1:
                    centerPoint = plane.Point1;
                    testVectors = new DoubleVector((plane.Point2 - plane.Point1).ToUnit(), (plane.Point0 - plane.Point1).ToUnit());
                    break;

                case 2:
                    centerPoint = plane.Point2;
                    testVectors = new DoubleVector((plane.Point0 - plane.Point2).ToUnit(), (plane.Point1 - plane.Point2).ToUnit());
                    break;

                default:
                    throw new ApplicationException("Unexpected cntr: " + cntr.ToString());
                }

                if (Math1D.IsNearZero(Vector3D.DotProduct(testVectors.Standard, testVectors.Orth)))
                {
                    break;
                }
            }

            #endregion

            double dot = Vector3D.DotProduct(testVectors.Standard, lookDirection.ToUnit());
            if (Math.Abs(dot) < THRESHOLD)
            {
                return(new RayHitTestParameters(centerPoint, testVectors.Standard));             // standard is fairly orhogonal to the camera's look direction, so only allow the part to slide along this axis
            }

            dot = Vector3D.DotProduct(testVectors.Orth, lookDirection.ToUnit());
            if (Math.Abs(dot) < THRESHOLD)
            {
                return(new RayHitTestParameters(centerPoint, testVectors.Orth));
            }

            return(null);
        }
Example #11
0
            public BoundryField(double startPercent, double strengthHalf, double exponent, Point3D mapMin, Point3D mapMax)
            {
                this.StartPercent = startPercent;
                this.StrengthHalf = strengthHalf;
                this.Exponent     = exponent;
                this.MapMin       = mapMin;
                this.MapMax       = mapMax;

                #region Initialize

                // Center point
                _center = new Point3D((this.MapMin.X + this.MapMax.X) / 2d, (this.MapMin.Y + this.MapMax.Y) / 2d, (this.MapMin.Z + this.MapMax.Z) / 2d);

                Vector3D offset   = this.MapMax - _center;
                double   maxValue = Math.Max(offset.X, Math.Max(offset.Y, offset.Z));

                // Boundries
                _boundryStop         = maxValue;
                _boundryStart        = _boundryStop * this.StartPercent;
                _boundryStartSquared = _boundryStart * _boundryStart;

                // force = c * x^2
                // c = force / x^2
                _equationConstant = this.StrengthHalf / Math.Pow((_boundryStop - _boundryStart) * .5d, this.Exponent);

                // Ratios
                if (Math1D.IsNearZero(offset.X))
                {
                    _ratioX = 1d;
                }
                else
                {
                    _ratioX = maxValue / offset.X;
                }

                if (Math1D.IsNearZero(offset.Y))
                {
                    _ratioY = 1d;
                }
                else
                {
                    _ratioY = maxValue / offset.Y;
                }

                if (Math1D.IsNearZero(offset.Z))
                {
                    _ratioZ = 1d;
                }
                else
                {
                    _ratioZ = maxValue / offset.Z;
                }

                #endregion
            }
Example #12
0
        // These are just crude test methods
        private void Update_Simplest()
        {
            var top = _neurons.OrderByDescending(o => o.Value).FirstOrDefault();

            if (top != null && !Math1D.IsNearZero(top.Value))
            {
                // Map this to the mouse plate
                Point point = new Point(top.Position.X * _distanceMult, top.Position.Y * _distanceMult);        // no need to look at Z.  The neurons should have been laid out on the XY plane

                _mousePlate.CurrentPoint2D = point;
            }
        }
Example #13
0
        /// <summary>
        /// This will emulate sounds coming from locations relative to the listener
        /// </summary>
        /// <remarks>
        /// Note that sound intensity doesn't diminish linearly from by distance, but by:
        ///    I = P/(4*pi*r^2)
        ///
        /// I is final intensity
        /// P is intensity at the source
        /// r is distance from the listener
        /// </remarks>
        /// <param name="volume">0 (none) to 1 (full)</param>
        /// <param name="balance">-1 (all left) to 1 (all right)</param>
        /// <param name="offset">The location of the sound relative to the listener</param>
        /// <param name="sourceVolume">0 (none) to 1 (full) - this is how loud the source would be at the listener</param>
        private void GetPositionalSoundSettings(out double volume, out double balance, Vector3D offset, double sourceVolume)
        {
            #region Calculate Volume

            // I won't range check the input volume - I'll leave it up to the media player to either bomb or cap it

            double intensityRatio = 1d;
            double offsetLength   = offset.Length;
            if (_distanceFalloffRatio == 0d || Math1D.IsNearZero(offsetLength))
            {
                volume = sourceVolume;
            }
            else
            {
                double distance = offsetLength * _distanceFalloffRatio;

                intensityRatio = 1d / (4d * Math.PI * distance * distance);    // I need this intensity when calculating balance
                volume         = sourceVolume * intensityRatio;
            }

            #endregion

            #region Calculate Balance

            // This means that if a sound is a distance of very near zero, and all the way to the left or right, then instead of +-1, it is +-.5
            const double MAXOPPOSITE_EAR = .5d;
            // When the intensity would be 75% of max, then I won't add anything to the opposite ear
            const double MAXOPPOSITE_DISTANCEINTENSITY = .75d;

            // Getting the angle (I don't want Z)
            double angle = Vector3D.AngleBetween(new Vector3D(1, 0, 0), new Vector3D(offset.X, offset.Y, 0));

            // cos(0) is 1, cos(90) is 0, cos(180) is -1.  Exactly what I need
            balance = Math.Cos(Math1D.DegreesToRadians(angle));

            //NOTE:  The problem with a pure cosine is that if a loud sound is sitting very near the person, but on the left, then in reality, the right
            // ear would hear something, but a simple cosine would be all the way -1
            //
            // So, I'm going to approach .5 for objects that are near (still on the left, but can be heard on the right)
            if (intensityRatio > MAXOPPOSITE_DISTANCEINTENSITY)
            {
                // So when the intensity is 1 (right next to the head), then I will give back .5
                // When the intensity is at the limit, then balance will be the pure cosine value
                // Anywhere in between is a linear interpelation
                balance = UtilityCore.GetScaledValue_Capped(balance, MAXOPPOSITE_EAR * balance, MAXOPPOSITE_DISTANCEINTENSITY, 1d, intensityRatio);
            }

            #endregion

            //TODO:  Take in relative velocity, then manipulate playback speed to emulate doppler (unless there is a way to change pitch directly)
        }
Example #14
0
        /// <summary>
        /// This takes the click point, and returns a point from 0 to 1
        /// </summary>
        private static Point GetScaledPoint(MouseEventArgs e, FrameworkElement relativeTo)
        {
            Point clickPoint = e.GetPosition(relativeTo);

            double width  = relativeTo.ActualWidth;
            double height = relativeTo.ActualHeight;

            if (Math1D.IsNearZero(width) || Math1D.IsNearZero(height))
            {
                return(new Point(0, 0));
            }

            return(new Point(clickPoint.X / width, clickPoint.Y / height));
        }
Example #15
0
        public bool Transfer()
        {
            //NOTE: This doesn't call the converter's update, that is done by the ship (it randomizes the order that they are updated)
            lock (_lock)
            {
                if (_converters.Length == 0)
                {
                    return(false);
                }

                // See how much volume all of the converters contain
                Tuple <double, double>[] used = _converters.Select(o => Tuple.Create(o.UsedVolume, o.MaxVolume)).ToArray();

                // If they are all greater than 50% capacity, then just leave now
                if (!used.Any(o => o.Item1 / o.Item2 < .5d))
                {
                    return(false);
                }

                // See how much quantity is needed
                double needFull   = used.Sum(o => o.Item2 - o.Item1);
                double needScaled = needFull * .99d;       // do this to avoid rounding errors causing overflow when distributing to multiple converters

                // Try to pull this much volume out of the cargo bays
                var cargo = _cargoBays.RemoveMineral_Volume(needScaled);

                if (Math1D.IsNearZero(cargo.Item1))
                {
                    // The cargo bays are empty
                    return(false);
                }

                if (_converters.Length == 1)
                {
                    // No need to distribute to multiple converters, just give everything to the one
                    Transfer_One(cargo.Item2);
                }
                else
                {
                    // Distriube evenly across the converters
                    //NOTE: Passing in needFull, because that's what used is based off of.  Transfer_Many calculates percents, and if the scaled value is passed
                    //in, the percents will be off.
                    Transfer_Many(cargo, used, needFull);
                }

                return(true);
            }
        }
Example #16
0
        private static double BuildTerrainSprtRadius(Point[] points)
        {
            // Get the average min distance between the points
            List <Tuple <int, int, double> > distances = new List <Tuple <int, int, double> >();

            for (int outer = 0; outer < points.Length - 1; outer++)
            {
                for (int inner = outer + 1; inner < points.Length; inner++)
                {
                    double distance = (points[outer] - points[inner]).LengthSquared;

                    distances.Add(Tuple.Create(outer, inner, distance));
                }
            }

            double avgDist;

            if (distances.Count == 0)
            {
                avgDist = points[0].ToVector().Length;
                if (Math1D.IsNearZero(avgDist))
                {
                    avgDist = .1;
                }
            }
            else
            {
                avgDist = Enumerable.Range(0, points.Length).
                          Select(o => distances.
                                 Where(p => p.Item1 == o || p.Item2 == o). // get the disances that mention this index
                                 Min(p => p.Item3)).                       // only keep the smallest of those distances
                          Average();                                       // get the average of all the mins

                avgDist = Math.Sqrt(avgDist);
            }

            // Get the distance of the farthest out neuron
            double maxDist = points.Max(o => o.ToVector().LengthSquared);

            maxDist = Math.Sqrt(maxDist);

            // Radius of the extra points will be the avg distance beyond that max
            return(maxDist + avgDist);
        }
Example #17
0
        private void Mineral_ApplyForceAndTorque(object sender, BodyApplyForceAndTorqueArgs e)
        {
            const double POW = 1d / 2.5d;

            Vector3D force = _gravityField.GetForce(e.Body.Position);

            double origLength = force.Length;

            if (Math1D.IsNearZero(origLength))
            {
                return;
            }

            //double newLength = Math.Sqrt(origLength);
            double newLength  = Math.Pow(origLength, POW);
            double multiplier = newLength / origLength;

            e.Body.AddForce(new Vector3D(force.X * multiplier, force.Y * multiplier, force.Z * multiplier));
        }
Example #18
0
        private void RedrawCanvas()
        {
            if (Math1D.IsNearZero(canvasMap.ActualWidth) || Math1D.IsNearZero(canvasMap.ActualHeight))
            {
                return;
            }

            Transform transform = GetMapToCanvasTransform();

            Effect effect = new DropShadowEffect()
            {
                Color       = Colors.Black,
                Opacity     = .25,
                BlurRadius  = 10,
                Direction   = 0,
                ShadowDepth = 0
            };

            canvasMap.Children.Clear();

            foreach (SpaceStation2D station in _map.GetItems <SpaceStation2D>(false))
            {
                Point3D position     = station.PositionWorld;
                Point   positionView = transform.Transform(new Point(position.X, -position.Y));

                DrawFlag(positionView, 30, 20, station.Flag, effect);
            }

            foreach (ShipPlayer ship in _map.GetItems <ShipPlayer>(false))
            {
                Point3D position     = ship.PositionWorld;
                Point   positionView = transform.Transform(new Point(position.X, -position.Y));

                Vector3D dir3D = ship.PhysicsBody.DirectionToWorld(new Vector3D(0, 1, 0));
                double   angle = Vector.AngleBetween(new Vector(dir3D.X, dir3D.Y), new Vector(0, 1));

                DrawTriangle(positionView, 10, 20, angle, _playerBrush, _playerStroke, 1, effect);
            }

            DrawAsteroidsMinerals(transform, true);

            ShowCameraView();
        }
Example #19
0
        /// <summary>
        /// This takes the click point, and returns a point from 0 to 1
        /// </summary>
        private static Point3D GetScaledPoint(MouseEventArgs e, FrameworkElement relativeTo, double depth, ViewDirection direction)
        {
            Point clickPoint = e.GetPosition(relativeTo);

            double width  = relativeTo.ActualWidth;
            double height = relativeTo.ActualHeight;

            if (Math1D.IsNearZero(width) || Math1D.IsNearZero(height))
            {
                // The window has no size
                return(new Point3D(0, 0, 0));
            }

            Point3D point = new Point3D(clickPoint.X / width, clickPoint.Y / height, depth);

            switch (direction)
            {
            case ViewDirection.Front:
                return(new Point3D(point.X, point.Y, point.Z));

            case ViewDirection.Right:
                return(new Point3D(1d - point.Z, point.Y, point.X));

            case ViewDirection.Left:
                return(new Point3D(point.Z, point.Y, 1d - point.X));

            case ViewDirection.Top:
                return(new Point3D(point.X, point.Z, 1d - point.Y));

            case ViewDirection.Bottom:
                return(new Point3D(point.X, 1d - point.Z, point.Y));

            case ViewDirection.Back:
                return(new Point3D(1d - point.X, point.Y, 1d - point.Z));

            default:
                throw new ApplicationException("Unknown ViewDirection: " + direction.ToString());
            }
        }
Example #20
0
        private Vector3D StraightToTarget_VelocityAware1Worker(Point3D chasePointWorld)
        {
            // Convert everything to local coords
            Point3D position        = new Point3D(0, 0, 0);
            Point3D chasePointLocal = this.PhysicsBody.PositionFromWorld(chasePointWorld);

            Vector3D directionToGo = chasePointLocal.ToVector() - position.ToVector();

            Vector3D axis;
            double   radians;

            #region Adjust for current velocity attempt1a

            Vector3D currentVelocity = this.VelocityWorld;

            if (!Math1D.IsNearZero(currentVelocity.LengthSquared))
            {
                currentVelocity = this.PhysicsBody.DirectionFromWorld(currentVelocity);

                Quaternion rotation = Math3D.GetRotation(directionToGo, currentVelocity);

                // This is how much to rotate direction to align with current velocity, I want to go against the current velocity (if aligned,
                // the angle will be zero, so negating won't make a difference)
                rotation = rotation.ToReverse();

                // If it's greater than 90 degrees, then just use the original direction (because it will pull the velocity in line
                // eventually)  I don't multiply by .5, because when it is very close to 90 degrees, the bot will thrash a bit
                if (Math.Abs(Math1D.DegreesToRadians(rotation.Angle)) < Math.PI * .4d)
                {
                    // Change the direction by the angle
                    directionToGo = rotation.GetRotatedVector(directionToGo);
                }
            }

            #endregion

            // Exit Function
            return(directionToGo);
        }
Example #21
0
 internal static double CalculateMagnitudePercent(double current, double max)
 {
     if (current > max)
     {
         return(1d);
     }
     else if (Math1D.IsNearZero(max))            // can't divide by zero
     {
         if (Math1D.IsNearZero(current))
         {
             return(0d);
         }
         else
         {
             return(1d);
         }
     }
     else
     {
         return(current / max);
     }
 }
Example #22
0
        private static bool AddPoisonSprtContainer(ref double mass, IContainer container, double ratio)
        {
            if (container == null)
            {
                return(false);
            }

            double needed = mass * ratio;
            double unmet  = container.RemoveQuantity(needed, false);

            // If unmet is zero, then there was enough in this container
            if (Math1D.IsNearZero(unmet))
            {
                mass = 0d;
                return(true);
            }

            // Reduce mass by the amount removed
            double removed = needed - unmet;

            mass -= removed * (1d / ratio);

            return(false);
        }
Example #23
0
        /// <summary>
        /// If this fluid hull is tied to a physics body, then this method must be called from within that body's apply force/torque callback
        /// </summary>
        public void Update()
        {
            if (AreForcesZero())
            {
                // All forces are zero, so don't waste the processor
                #region Clear Forces

                if (_triangles != null && !_areForcesSet)               // added this so I don't waste the processor clearing forces on a dead object each frame
                {
                    foreach (FluidTriangle triangle in _triangles)
                    {
                        triangle.NextFrame(null);
                    }

                    _areForcesSet = true;
                }

                #endregion
                return;
            }

            if (!_areForcePointsChosen)
            {
                // First time init
                ChooseForcePoints();
                _areForcePointsChosen = true;
            }

            // Transform the points to world coords
            Point3D[] worldPoints = GetTrianglePointsWorld();

            foreach (FluidTriangle triangle in _triangles)      // AreForcesZero verified that _triangles isn't null
            {
                triangle.NextFrame(worldPoints);

                Point3D[] forcesAt = triangle.GetForceAt();
                if (forcesAt == null)
                {
                    continue;
                }

                Vector3D[] forces = new Vector3D[forcesAt.Length];

                // Can't use triangle.Normal, because it's still in model coords
                Vector3D normal = Vector3D.CrossProduct(worldPoints[triangle.Index2] - worldPoints[triangle.Index1],
                                                        worldPoints[triangle.Index0] - worldPoints[triangle.Index1]);

                //double avgNormalLength = triangle.Normal.Length / forcesAt.Length;
                double avgNormalLength = normal.Length / forcesAt.Length;

                Tuple <Vector3D, double>[] flowsAt = this.Field.GetForce(forcesAt, _sampleRadius);

                for (int cntr = 0; cntr < forcesAt.Length; cntr++)
                {
                    #region Sample Point

                    // Calculate the velocity of the triangle at this point
                    Vector3D velocityAtPoint = new Vector3D(0, 0, 0);
                    if (this.Body != null)
                    {
                        velocityAtPoint = this.Body.GetVelocityAtPoint(forcesAt[cntr]);
                    }

                    Vector3D flowAtPoint = flowsAt[cntr].Item1 - velocityAtPoint;

                    // If it's convex/uniform, only allow forces that flow into the hull
                    if (!this.IsClosedConvexInUniformField || Vector3D.DotProduct(flowAtPoint, normal) < 0)
                    {
                        // Scale the normal based on how much of the flow is along it
                        forces[cntr] = flowAtPoint.GetProjectedVector(normal);          // the length returned is from zero to flowAtPoint.Length.  avgNormal's length is ignored, only its direction

                        // Scale the force by the area of the triangle (cross gives area of parallelagram, so taking half)
                        // Also scale by viscocity
                        forces[cntr] *= avgNormalLength * (.5d * flowsAt[cntr].Item2);

                        // Apply the force to the body
                        if (this.Body != null && !Math1D.IsNearZero(forces[cntr].LengthSquared))
                        {
                            this.Body.AddForceAtPoint(forces[cntr], forcesAt[cntr]);
                        }
                    }

                    #endregion
                }

                triangle.SetForces(forces);             // this is only stored here for debugging, etc (physics doesn't look at it)
            }
        }
Example #24
0
        private void InvalidateGeometry()
        {
            Vector3D direction       = _toPoint - _fromPoint;
            double   directionLength = direction.Length;

            if (Math1D.IsInvalid(directionLength) || Math1D.IsNearZero(directionLength))
            {
                _lineModel.Transform = new ScaleTransform3D(0, 0, 0);

                if (_fromArrowModel != null)
                {
                    _fromArrowModel.Transform = new ScaleTransform3D(0, 0, 0);
                }

                if (_toArrowModel != null)
                {
                    _toArrowModel.Transform = new ScaleTransform3D(0, 0, 0);
                }
                return;
            }

            Vector3D directionUnit   = new Vector3D();
            double   arrowWidth      = 0;
            double   arrowLength     = 0;
            double   halfArrowLength = 0;

            if ((_fromArrowModel != null && _fromArrowPercent != null) || (_toArrowModel != null && _toArrowPercent != null))       // they should both be null or not null together, just being safe
            {
                directionUnit   = direction.ToUnit();
                arrowWidth      = _thickness * _arrowBaseMult * _arrowSizeMult;
                arrowLength     = _thickness * _arrowHeightMult * _arrowSizeMult;
                halfArrowLength = arrowLength / 2;
            }

            #region line

            double length = directionLength;

            // Reduce the length if the arrow is at the end so that the line graphic doesn't poke through the arrow graphic
            double?fromOffset = null;
            if (_fromArrowModel != null && _fromArrowPercent != null && _fromArrowPercent.Value.IsNearValue(1))
            {
                fromOffset = halfArrowLength;
                length    -= halfArrowLength;
            }

            if (_toArrowModel != null && _toArrowPercent != null && _toArrowPercent.Value.IsNearValue(1))
            {
                length -= halfArrowLength;
            }

            Transform3DGroup transform = new Transform3DGroup();

            if (length > Math1D.NEARZERO)
            {
                transform.Children.Add(new ScaleTransform3D(_thickness, _thickness, length));

                if (fromOffset != null)
                {
                    transform.Children.Add(new TranslateTransform3D(0, 0, fromOffset.Value));
                }

                transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(_initialOrientation, StaticRandom.NextDouble(360d))));     // spin
                transform.Children.Add(new RotateTransform3D(new QuaternionRotation3D(Math3D.GetRotation(_initialOrientation, direction))));
                transform.Children.Add(new TranslateTransform3D(_fromPoint.ToVector()));
            }
            else
            {
                // The arrows are visible, but there's not enough length to show the line graphic
                transform.Children.Add(new ScaleTransform3D(0, 0, 0));
            }

            _lineModel.Transform = transform;

            #endregion
            #region from arrow

            if (_fromArrowModel != null && _fromArrowPercent != null)
            {
                Vector3D arrowOffset   = directionUnit * arrowLength;
                Point3D  arrowPosition = _fromPoint + (direction * (1d - _fromArrowPercent.Value));
                arrowPosition += arrowOffset;       // this is because the tip of the arrow should be that the percent, not the base

                transform = new Transform3DGroup();

                transform.Children.Add(new ScaleTransform3D(arrowWidth, arrowWidth, arrowLength));
                transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(_initialOrientation, StaticRandom.NextDouble(360d))));      // spin
                transform.Children.Add(new RotateTransform3D(new QuaternionRotation3D(Math3D.GetRotation(_initialOrientation, -direction))));
                transform.Children.Add(new TranslateTransform3D(arrowPosition.ToVector()));

                _fromArrowModel.Transform = transform;
            }

            #endregion
            #region to arrow

            if (_toArrowModel != null && _toArrowPercent != null)
            {
                Vector3D arrowOffset   = directionUnit * arrowLength;
                Point3D  arrowPosition = _fromPoint + (direction * _toArrowPercent.Value);
                arrowPosition -= arrowOffset;       // this is because the tip of the arrow should be that the percent, not the base

                transform = new Transform3DGroup();

                transform.Children.Add(new ScaleTransform3D(arrowWidth, arrowWidth, arrowLength));
                transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(_initialOrientation, StaticRandom.NextDouble(360d))));      // spin
                transform.Children.Add(new RotateTransform3D(new QuaternionRotation3D(Math3D.GetRotation(_initialOrientation, direction))));
                transform.Children.Add(new TranslateTransform3D(arrowPosition.ToVector()));

                _toArrowModel.Transform = transform;
            }

            #endregion
        }
Example #25
0
        /// <summary>
        /// This returns a visual of a graph that can be added to a canvas
        /// </summary>
        public static IEnumerable <UIElement> GetGradientGraph(double width, double height, GradientEntry[] gradient, Color fill, Color stroke)
        {
            if (gradient == null || gradient.Length <= 1)       // need at least two for a gradient
            {
                return(new UIElement[0]);
            }
            else if (width.IsNearZero() || height.IsNearZero())
            {
                return(new UIElement[0]);
            }

            List <UIElement> retVal = new List <UIElement>();

            double maxPercent = gradient.Max(o => o.Percent);

            if (maxPercent > 1)
            {
                #region 100% line

                // Draw a dashed line at 1 (showing the 100% mark)
                Color color100 = UtilityWPF.AlphaBlend(UtilityWPF.AlphaBlend(stroke, Colors.Gray, .85), Colors.Transparent, .66);

                double y100 = ((maxPercent - 1) / maxPercent) * height;

                Line line100 = new Line()
                {
                    Stroke          = new SolidColorBrush(color100),
                    StrokeThickness = 1,
                    StrokeDashArray = new DoubleCollection(new[] { 4d, 2d }),
                    X1 = 0,
                    X2 = width,
                    Y1 = y100,
                    Y2 = y100,
                };

                retVal.Add(line100);

                #endregion
            }

            if (maxPercent < 1)
            {
                // Do this so the graph is to scale (doesn't always go to the top)
                maxPercent = 1;
            }

            double lastGradX = gradient[gradient.Length - 1].Distance;
            if (!Math1D.IsNearZero(lastGradX) && lastGradX > 0)
            {
                Polyline polyLine = new Polyline();
                Polygon  polyFill = new Polygon();

                polyLine.Stroke          = new SolidColorBrush(stroke);
                polyLine.StrokeThickness = 2;

                polyFill.Fill = new SolidColorBrush(fill);

                //NOTE: gradient must be sorted on Item1
                double xScale = width / lastGradX;

                for (int cntr = 0; cntr < gradient.Length; cntr++)
                {
                    double x = gradient[cntr].Distance * xScale;
                    double y = ((maxPercent - gradient[cntr].Percent) / maxPercent) * height;

                    polyLine.Points.Add(new Point(x, y));
                    polyFill.Points.Add(new Point(x, y));
                }

                // Circle back fill to make a polygon
                polyFill.Points.Add(new Point(polyFill.Points[polyFill.Points.Count - 1].X, height));
                polyFill.Points.Add(new Point(polyFill.Points[0].X, height));

                retVal.Add(polyFill);
                retVal.Add(polyLine);
            }

            return(retVal);
        }
Example #26
0
        private static void DrawFieldSprtDoIt(byte[] pixels, double[] ink, bool[] blocked, int size, byte[] colorZFront, byte[] colorZBack, AxisFor pixelX, AxisFor pixelY, AxisFor pixelZ)
        {
            List <Mapping_2D_1D> flattened = new List <Mapping_2D_1D>();

            // Setup the pixel array
            //for (int y2D = pixelY.Start; pixelY.IsPos ? y2D <= pixelY.Stop : y2D >= pixelY.Stop; y2D += pixelY.Increment)
            foreach (int y2D in pixelY.Iterate())
            {
                int offsetY = pixelY.GetValueForOffset(y2D) * size;

                //for (int x2D = pixelX.Start; pixelX.IsPos ? x2D <= pixelX.Stop : x2D >= pixelX.Stop; x2D += pixelX.Increment)
                foreach (int x2D in pixelX.Iterate())
                {
                    int offset = offsetY + pixelX.GetValueForOffset(x2D);

                    flattened.Add(new Mapping_2D_1D(x2D, y2D, offset));
                }
            }

            // Each pixel of the output bitmap can be added up independently, so employ some threading
            flattened.AsParallel().ForAll(o =>
            {
                //TODO: Color is 6.5 times slower than byte array
                List <byte[]> colorColumn = new List <byte[]>();

                for (int z2D = pixelZ.Start; pixelZ.IsPos?z2D <= pixelZ.Stop : z2D >= pixelZ.Stop; z2D += pixelZ.Increment)
                {
                    int x = -1, y = -1, z = -1;
                    pixelX.Set3DIndex(ref x, ref y, ref z, o.X);
                    pixelY.Set3DIndex(ref x, ref y, ref z, o.Y);
                    pixelZ.Set3DIndex(ref x, ref y, ref z, z2D);

                    int index = FluidField3D.Get1DIndex(x, y, z, size);
                    if (blocked[index])
                    {
                        // Blocked cells are all white, so save the overlay method a bit of work, and throw out everything behind this
                        colorColumn.Clear();
                        colorColumn.Add(new byte[] { 255, 255, 255, 255 });
                        continue;
                    }

                    double inkCell = ink[index];

                    if (Math1D.IsNearZero(inkCell))
                    {
                        continue;
                    }

                    byte[] depthColor = UtilityWPF.AlphaBlend(colorZBack, colorZFront, UtilityCore.GetScaledValue_Capped(0, 1, 0, size - 1, z));

                    int alpha = Convert.ToInt32(Math.Round(inkCell * 255));
                    if (alpha < 0)
                    {
                        alpha = 0;
                    }
                    else if (alpha > 255)
                    {
                        alpha = 255;
                    }

                    colorColumn.Add(new byte[] { Convert.ToByte(alpha), depthColor[1], depthColor[2], depthColor[3] });
                }

                byte[] color = colorColumn.Count > 0 ? UtilityWPF.OverlayColors(colorColumn) : new byte[] { 0, 0, 0, 0 };

                pixels[o.Offset1D * 4 + 0] = color[3];       // Blue
                pixels[o.Offset1D * 4 + 1] = color[2];       // Green
                pixels[o.Offset1D * 4 + 2] = color[1];       // Red
                pixels[o.Offset1D * 4 + 3] = color[0];       // Alpha
            });
        }
Example #27
0
        private static Model3DGroup GetModel_WoodIron(WeaponSpikeBallDNA dna, WeaponSpikeBallDNA finalDNA, WeaponMaterialCache materials)
        {
            Model3DGroup retVal = new Model3DGroup();

            Random rand = StaticRandom.GetRandomForThread();
            var    from = dna.KeyValues;
            var    to   = finalDNA.KeyValues;

            double spikeLength = dna.Radius * 1.4d;
            double ballRadius  = dna.Radius * 1d;

            double spikeRadius = dna.Radius * .2;

            #region Ball

            System.Windows.Media.Media3D.Material material = materials.Ball_Wood;     // the property get returns a slightly random color

            GeometryModel3D geometry = new GeometryModel3D();

            geometry.Material     = material;
            geometry.BackMaterial = material;

            // Create a convex hull out of semi evenly distibuted points
            int numHullPoints      = Convert.ToInt32(WeaponDNA.GetKeyValue("numHullPoints", from, to, rand.Next(20, 50)));
            TriangleIndexed[] ball = Math3D.GetConvexHull(Math3D.GetRandomVectors_SphericalShell_EvenDist(numHullPoints, ballRadius, .03, 10).Select(o => o.ToPoint()).ToArray());

            geometry.Geometry = UtilityWPF.GetMeshFromTriangles(ball);

            retVal.Children.Add(geometry);

            #endregion

            // These are placed where the rings are, to push spikes away (so that spikes don't poke through the rings)
            List <Vector3D> staticPoints = new List <Vector3D>();

            #region Rings

            material = materials.Spike_Iron;     // the property get returns a slightly random color

            // 0, 1 or 2 rings.  Higher chance of 0 than 2
            int numRings = Convert.ToInt32(WeaponDNA.GetKeyValue("numRings", from, to, Math.Floor(rand.NextPow(2, 2.3))));

            double[] zs = new double[0];

            switch (numRings)
            {
            case 0:
                break;

            case 1:
                zs = new double[] { WeaponDNA.GetKeyValue("ringZ1", from, to, Math1D.GetNearZeroValue(ballRadius * .75)) };
                break;

            case 2:
                double z1 = WeaponDNA.GetKeyValue("ringZ1", from, to, Math1D.GetNearZeroValue(ballRadius * .75));
                double z2 = 0;

                if (from == null || !from.TryGetValue("ringZ2", out z2))
                {
                    do
                    {
                        z2 = Math1D.GetNearZeroValue(ballRadius * .75);
                    } while (Math.Abs(z1 - z2) < ballRadius * .4);

                    to.Add("ringZ2", z2);
                }

                zs = new double[] { z1, z2 };
                break;

            default:
                throw new ApplicationException("Unexpected number of rings: " + numRings.ToString());
            }

            // Build the rings at the z offsets that were calculated above
            for (int cntr = 0; cntr < zs.Length; cntr++)
            {
                retVal.Children.Add(GetModel_WoodIron_Ring_Band(ballRadius, zs[cntr], material, ball, from, to, "ringZ" + cntr.ToString()));

                // Store points at the rings
                double ringRadiusAvg = Math.Sqrt((ballRadius * ballRadius) - (zs[cntr] * zs[cntr]));
                staticPoints.AddRange(Math2D.GetCircle_Cached(7).Select(o => (o.ToVector() * ringRadiusAvg).ToVector3D(zs[cntr])));
            }

            #endregion

            #region Spikes

            Vector3D[] staticPointsArr = staticPoints.Count == 0 ? null : staticPoints.ToArray();
            double[]   staticRepulse   = staticPoints.Count == 0 ? null : Enumerable.Range(0, staticPoints.Count).Select(o => .005d).ToArray();

            int numSpikes = Convert.ToInt32(WeaponDNA.GetKeyValue("numSpikes", from, to, rand.Next(8, 14)));

            Vector3D[] spikeLocations;
            if (from != null && from.ContainsKey("spikeLoc0X"))
            {
                spikeLocations = new Vector3D[numSpikes];

                for (int cntr = 0; cntr < numSpikes; cntr++)
                {
                    string prefix = "spikeLoc" + cntr.ToString();
                    spikeLocations[cntr] = new Vector3D(to[prefix + "X"], to[prefix + "Y"], to[prefix + "Z"]);
                }
            }
            else
            {
                spikeLocations = Math3D.GetRandomVectors_SphericalShell_EvenDist(numSpikes, ballRadius, .03, 10, null, staticPointsArr, staticRepulse);

                for (int cntr = 0; cntr < numSpikes; cntr++)
                {
                    string prefix = "spikeLoc" + cntr.ToString();
                    to.Add(prefix + "X", spikeLocations[cntr].X);
                    to.Add(prefix + "Y", spikeLocations[cntr].Y);
                    to.Add(prefix + "Z", spikeLocations[cntr].Z);
                }
            }

            for (int cntr = 0; cntr < spikeLocations.Length; cntr++)
            {
                material = materials.Spike_Iron;     // the property get returns a slightly random color

                geometry = new GeometryModel3D();

                geometry.Material     = material;
                geometry.BackMaterial = material;

                RotateTransform3D transform = new RotateTransform3D(new QuaternionRotation3D(Math3D.GetRotation(new Vector3D(0, 0, 1), spikeLocations[cntr])));       // the tube builds along z

                List <TubeRingBase> rings = new List <TubeRingBase>();

                double spikeRadiusA = WeaponDNA.GetKeyValue("spikeRad" + cntr.ToString(), from, to, rand.NextPercent(spikeRadius, .33));

                rings.Add(new TubeRingRegularPolygon(0, false, spikeRadiusA, spikeRadiusA, false));
                rings.Add(new TubeRingPoint(WeaponDNA.GetKeyValue("spikeLen" + cntr.ToString(), from, to, rand.NextDouble(spikeLength * .9, spikeLength * 1.1)), false));

                int  numSegments = Convert.ToInt32(WeaponDNA.GetKeyValue("spikeSegs" + cntr.ToString(), from, to, rand.Next(3, 6)));
                bool isSoft      = Math1D.IsNearZero(WeaponDNA.GetKeyValue("spikeSoft" + cntr.ToString(), from, to, rand.Next(2)));
                geometry.Geometry = UtilityWPF.GetMultiRingedTube(numSegments, rings, isSoft, false, transform);

                retVal.Children.Add(geometry);
            }

            #endregion

            return(retVal);
        }
Example #28
0
        private static Tuple <Dot, Vector3D>[] GetForces(DotVisuals dots, bool useOutput, double mult)
        {
            // Figure out which set of distances to use
            var distances = useOutput ? dots.Distances_Output : dots.Distances_Input;

            #region Calculate forces

            Tuple <Dot, Vector3D>[] forces = distances.
                                             //AsParallel().     //TODO: if distances.Length > threshold, do this in parallel
                                             SelectMany(o =>
            {
                // Spring from 1 to 2
                Vector3D spring     = o.Item2.Position - o.Item1.Position;
                double springLength = spring.Length;

                double difference = o.Item3 - springLength;
                difference       *= mult;

                if (Math1D.IsNearZero(springLength))
                {
                    spring = Math3D.GetRandomVector_Spherical_Shell(Math.Abs(difference));
                }
                else
                {
                    spring = spring.ToUnit() * Math.Abs(difference);
                }

                if (difference > 0)
                {
                    // Gap needs to be bigger, push them away (default is closing the gap)
                    spring = -spring;
                }

                return(new[]
                {
                    Tuple.Create(o.Item1, spring),
                    Tuple.Create(o.Item2, -spring)
                });
            }).
                                             ToArray();

            #endregion

            // Give them a very slight pull toward the origin so that the cloud doesn't drift away
            Point3D center     = Math3D.GetCenter(dots.Dots.Select(o => o.Position));
            double  centerMult = mult * -5;

            Vector3D centerPullForce = center.ToVector() * centerMult;

            // Group by dot
            var grouped = forces.
                          GroupBy(o => o.Item1.Token);

            return(grouped.
                   Select(o =>
            {
                Vector3D sum = centerPullForce;
                Dot dot = null;

                foreach (var force in o)
                {
                    dot = force.Item1;
                    sum += force.Item2;
                }

                return Tuple.Create(dot, sum);
            }).
                   ToArray());
        }
Example #29
0
        private void Transfer_Many_DistributeGroup(List <Cargo> cargo, double[] percents)
        {
            double sumVolume = cargo.Sum(o => o.Volume);

            // This is used to keep track of how much each converter has left to go
            double[] remaining = percents.Select(o => sumVolume * o).ToArray();

            int infiniteLoopCntr = 0;

            while (cargo.Count > 0)
            {
                bool foundOne = false;

                #region Whole matches

                // Shoot through the cargo, looking for ones that will fit exactly
                int index = 0;
                while (index < cargo.Count)
                {
                    bool wasRemoved = false;
                    for (int cntr = 0; cntr < _converters.Length; cntr++)
                    {
                        if (remaining[cntr] >= cargo[index].Volume)
                        {
                            if (_converters[cntr].Add(cargo[index]))        // This should never come back false unless there is a rounding error (or something else added to the converter outside of this class)
                            {
                                remaining[cntr] -= cargo[index].Volume;
                                cargo.RemoveAt(index);
                                wasRemoved = true;
                                foundOne   = true;
                                break;
                            }
                        }
                    }

                    if (!wasRemoved)
                    {
                        index++;
                    }
                }

                #endregion

                if (!foundOne)
                {
                    #region Divide one cargo

                    // Nothing was moved during the previous pass, so find a converter that still needs to be filled, and fill it exactly

                    if (cargo.Count == 0)
                    {
                        throw new ApplicationException("Execution shouldn't have gotten into this if statement if there is no more cargo to distribute");
                    }

                    for (int cntr = 0; cntr < remaining.Length; cntr++)
                    {
                        if (remaining[cntr] > 0d && !Math1D.IsNearZero(remaining[cntr]))
                        {
                            if (cargo[0].Volume < remaining[cntr])
                            {
                                throw new ApplicationException("The previous pass should have caught this");
                            }

                            Cargo splitCargo = cargo[0].Clone();

                            splitCargo.Volume = remaining[cntr];
                            cargo[0].Volume  -= remaining[cntr];
                            if (cargo[0].Volume.IsNearZero())
                            {
                                cargo.RemoveAt(0);
                            }

                            if (!_converters[cntr].Add(splitCargo))
                            {
                                throw new ApplicationException(string.Format("Couldn't add cargo to the converter: {0}, {1}", remaining[cntr].ToString(), (_converters[cntr].MaxVolume - _converters[cntr].UsedVolume).ToString()));
                            }

                            remaining[cntr] = 0d;
                            break;
                        }
                    }

                    #endregion
                }

                infiniteLoopCntr++;
                if (infiniteLoopCntr > 100)
                {
                    // This happens when the cargo volumes are almost zero (but not quite below the NEARZERO threshold.  Don't bother trying to
                    // distribute such a tiny amount, just exit
                    //if(cargo.All(o => o.Volume.IsNearZero()))
                    if (cargo.All(o => Math.Abs(o.Volume) < UtilityCore.NEARZERO * 100))
                    {
                        return;
                    }

                    throw new ApplicationException("Infinite loop detected");
                }
            }
        }