Esempio n. 1
0
 protected internal override void Save(XmlWriter writer)
 {
     writer.WriteStartElement("line");
     writer.WriteAttributeString("start", ManeuveringBoard.FormatXmlVector(Start));
     writer.WriteAttributeString("end", ManeuveringBoard.FormatXmlVector(End));
     writer.WriteEndElement();
 }
Esempio n. 2
0
        internal string GetStatusText()
        {
            Vector2 vector = Vector;

            return("Line: " + ManeuveringBoard.GetAngleString(ManeuveringBoard.SwapBearing(vector.Angle)) + ", " +
                   Board.GetDistanceString(vector.Length));
        }
Esempio n. 3
0
        public InterceptForm(UnitShape unit, UnitShape target, UnitSystem unitSystem, bool disableControls) : this()
        {
            if (target != null)
            {
                if (unit != null)
                {
                    txtBearing.Text    = ManeuveringBoard.GetAngleString(ManeuveringBoard.AngleBetween(unit.Position, target.Position));
                    txtRange.Text      = ManeuveringBoard.GetDistanceString((target.Position - unit.Position).Length, unitSystem);
                    txtBearing.Enabled = txtRange.Enabled = !disableControls;
                    txtSpeed.Select();
                }

                txtCourse.Text      = ManeuveringBoard.GetAngleString(target.Direction);
                txtTargetSpeed.Text = ManeuveringBoard.GetSpeedString(target.Speed, unitSystem);
                txtCourse.Enabled   = txtTargetSpeed.Enabled = !disableControls;
            }

            if (unit == null)
            {
                radVector.Enabled = radWaypoint.Enabled = btnOK.Enabled = false;
            }

            this.unit       = unit;
            this.target     = target;
            this.unitSystem = unitSystem;
            UpdateSolution();
        }
Esempio n. 4
0
 protected internal override void Save(XmlWriter writer)
 {
     writer.WriteStartElement("waypoint");
     writer.WriteAttributeString("position", ManeuveringBoard.FormatXmlVector(Position));
     base.Save(writer);
     writer.WriteEndElement();
 }
Esempio n. 5
0
        protected internal override void Save(XmlWriter writer)
        {
            writer.WriteStartElement("unit");
            writer.WriteAttributeString("id", GetXmlId());
            if (!string.IsNullOrEmpty(Name))
            {
                writer.WriteAttributeString("name", Name);
            }
            writer.WriteAttributeString("position", ManeuveringBoard.FormatXmlVector(Position));
            writer.WriteAttribute("course", Direction);
            writer.WriteAttribute("speed", Speed);

            string typeString = Type.ToString();

            writer.WriteAttributeString("type", typeString.Substring(0, 1).ToLowerInvariant() + typeString.Substring(1));

            if (TMASolution != null)
            {
                TMASolution.Save(writer);
            }

            if (Children.Count != 0)
            {
                writer.WriteStartElement("children");
                foreach (Shape shape in Children)
                {
                    shape.Save(writer);
                }
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }
Esempio n. 6
0
 protected internal override void Save(XmlWriter writer)
 {
     writer.WriteStartElement("circle");
     writer.WriteAttributeString("position", ManeuveringBoard.FormatXmlVector(Position));
     writer.WriteAttribute("radius", Radius);
     writer.WriteEndElement();
 }
Esempio n. 7
0
        public static LineShape Load(XmlReader reader)
        {
            LineShape line = new LineShape();

            line.Start = ManeuveringBoard.ParseXmlPoint(reader.GetStringAttribute("start"));
            line.End   = ManeuveringBoard.ParseXmlPoint(reader.GetStringAttribute("end"));
            return(line);
        }
Esempio n. 8
0
            public override void Update(Shape shape, BoardPoint dragStart, BoardPoint dragPoint)
            {
                UnitShape unit   = (UnitShape)shape;
                Vector2   vector = dragPoint - dragStart;

                unit.Direction = ManeuveringBoard.SwapBearing(vector.Angle);
                unit.Speed     = vector.Length * (1.0 / ManeuveringBoard.VectorTime);
            }
Esempio n. 9
0
        public static CircleShape Load(XmlReader reader)
        {
            CircleShape circle = new CircleShape();

            circle.Position = ManeuveringBoard.ParseXmlPoint(reader.GetStringAttribute("position"));
            circle.Radius   = reader.GetDoubleAttribute("radius");
            return(circle);
        }
Esempio n. 10
0
 internal void SetBoard(ManeuveringBoard board)
 {
     Board = board;
     foreach (Shape descendant in EnumerateDescendants())
     {
         descendant.Board = board;
     }
 }
Esempio n. 11
0
        public static Waypoint Load(XmlReader reader)
        {
            Waypoint shape = new Waypoint();

            LoadPositionalData(shape, reader);
            shape.Position = ManeuveringBoard.ParseXmlPoint(reader.GetStringAttribute("position"));
            return(shape);
        }
Esempio n. 12
0
 public BackgroundScaleForm(double pixels, double meters, UnitSystem unitSystem) : this()
 {
     lblPixels.Text       = pixels.ToString("0.##") + " pixels represents...";
     txtLength.Text       = ManeuveringBoard.GetDistanceString(meters, unitSystem);
     txtLength.Tag        = meters;
     txtLength.WasChanged = false;
     this.unitSystem      = unitSystem;
 }
Esempio n. 13
0
        public static PointObservation Load(XmlReader reader, Dictionary <Observation, string> observers)
        {
            PointObservation shape = new PointObservation();

            LoadPositionalData(shape, reader);
            shape.Position = ManeuveringBoard.ParseXmlPoint(reader.GetStringAttribute("position"));
            observers.Add(shape, reader.GetAttribute("observer"));
            return(shape);
        }
Esempio n. 14
0
        void FillRelativeTo(ChangeTrackingTextBox txtBearing, ChangeTrackingTextBox txtDistance, Point2 relativePoint)
        {
            double angle = ManeuveringBoard.AngleBetween(relativePoint, posDataPoint.Value);

            txtBearing.Text = (angle * MathConst.RadiansToDegrees).ToString("0.##");
            txtBearing.Tag  = angle;
            double distance = relativePoint.DistanceTo(posDataPoint.Value);

            txtDistance.Text      = ManeuveringBoard.GetDistanceString(distance, unitSystem);
            txtDistance.Tag       = distance;
            txtBearing.WasChanged = txtDistance.WasChanged = false;
        }
Esempio n. 15
0
 public BoardOptionsForm(ManeuveringBoard board) : this()
 {
     chkShowAllObservations.Checked = board.ShowAllObservations;
     cmbUnitSystem.SelectedIndex    = (int)board.UnitSystem;
     btnBackground.BackColor        = board.BackColor;
     btnObservations.BackColor      = board.ObservationColor;
     btnReference.BackColor         = board.ReferenceColor;
     btnScale1.BackColor            = board.ScaleColor1;
     btnScale2.BackColor            = board.ScaleColor2;
     btnSelected.BackColor          = board.SelectedColor;
     btnTMA.BackColor        = board.TMAColor;
     btnUnselected.BackColor = board.UnselectedColor;
 }
Esempio n. 16
0
        protected static bool TryParseLength(string text, UnitSystem unitSystem, out double meters)
        {
            Match m = lengthRe.Match(text);

            if (!m.Success || !double.TryParse(m.Groups["number"].Value, out meters))
            {
                meters = 0;
                return(false);
            }
            else
            {
                LengthUnit unit;
                switch (m.Groups["unit"].Value.ToLowerInvariant())
                {
                case "ft": unit = LengthUnit.Foot; break;

                case "km": unit = LengthUnit.Kilometer; break;

                case "kyd": unit = LengthUnit.Kiloyard; break;

                case "m": unit = LengthUnit.Meter; break;

                case "mi": unit = LengthUnit.Mile; break;

                case "nm":
                case "nmi": unit = LengthUnit.NauticalMile; break;

                case "yd": unit = LengthUnit.Yard; break;

                default:
                    switch (unitSystem)
                    {
                    case UnitSystem.Imperial: unit = LengthUnit.Mile; break;

                    case UnitSystem.Metric: unit = LengthUnit.Kilometer; break;

                    case UnitSystem.NauticalImperial:
                    case UnitSystem.NauticalMetric: unit = LengthUnit.NauticalMile; break;

                    default: unit = LengthUnit.Meter; break;
                    }
                    break;
                }
                meters = ManeuveringBoard.ConvertFromUnit(meters, unit);
                return(true);
            }
        }
Esempio n. 17
0
        public static new UnitShape Load(XmlReader reader, Dictionary <Observation, string> observers, Dictionary <string, UnitShape> unitsById)
        {
            UnitShape shape = new UnitShape();

            shape.Name      = reader.GetAttribute("name");
            shape.Position  = ManeuveringBoard.ParseXmlPoint(reader.GetStringAttribute("position"));
            shape.Direction = reader.GetDoubleAttribute("course");
            shape.Speed     = reader.GetDoubleAttribute("speed");
            shape.Type      = Utility.ParseEnum <UnitShapeType>(reader.GetStringAttribute("type", "unknown"), true);

            string id = reader.GetAttribute("id");

            if (!string.IsNullOrEmpty(id))
            {
                unitsById.Add(id, shape);
            }

            if (!reader.IsEmptyElement)
            {
                reader.Read();
                while (reader.NodeType == XmlNodeType.Element)
                {
                    if (reader.LocalName == "tmaSolution")
                    {
                        shape.TMASolution = TMASolution.Load(reader);
                    }
                    else if (reader.LocalName == "children" && !reader.IsEmptyElement)
                    {
                        reader.Read(); // move to either the first child or the end element
                        while (reader.NodeType == XmlNodeType.Element)
                        {
                            shape.Children.Add(Shape.Load(reader, observers, unitsById));
                        }
                        reader.ReadEndElement();
                    }
                    else
                    {
                        throw new InvalidDataException("Expected element " + reader.LocalName);
                    }
                }
            }

            return(shape);
        }
Esempio n. 18
0
        protected static bool TryParseSpeed(string text, UnitSystem unitSystem, out double metersPerSecond)
        {
            Match m = speedRe.Match(text);

            if (!m.Success || !double.TryParse(m.Groups["number"].Value, out metersPerSecond))
            {
                metersPerSecond = 0;
                return(false);
            }
            else
            {
                SpeedUnit unit;
                switch (m.Groups["unit"].Value.ToLowerInvariant())
                {
                case "kn":
                case "kt":
                case "kts": unit = SpeedUnit.Knots; break;

                case "kph": unit = SpeedUnit.KilometersPerHour; break;

                case "mph": unit = SpeedUnit.MilesPerHour; break;

                case "mps":
                case "m/s": unit = SpeedUnit.MetersPerSecond; break;

                default:
                    switch (unitSystem)
                    {
                    case UnitSystem.Imperial: unit = SpeedUnit.MilesPerHour; break;

                    case UnitSystem.Metric: unit = SpeedUnit.KilometersPerHour; break;

                    case UnitSystem.NauticalImperial:
                    case UnitSystem.NauticalMetric: unit = SpeedUnit.Knots; break;

                    default: unit = SpeedUnit.MetersPerSecond; break;
                    }
                    break;
                }
                metersPerSecond = ManeuveringBoard.ConvertFromUnit(metersPerSecond, unit);
                return(true);
            }
        }
Esempio n. 19
0
        public ShapeDataForm(Shape shape, UnitSystem unitSystem) : this()
        {
            if (shape == null)
            {
                throw new ArgumentNullException();
            }

            this.unitSystem = unitSystem;

            txtName.Text   = shape.Name;
            lblParent.Text = shape.Parent == null ? "<none>" : string.IsNullOrEmpty(shape.Parent.Name) ? "<unnamed shape>" : shape.Parent.Name;

            UnitShape unit = shape as UnitShape;

            if (unit != null)
            {
                txtSize.Enabled = false;

                txtDirection.Tag      = unit.Direction;
                txtDirection.Text     = (unit.Direction * MathConst.RadiansToDegrees).ToString("0.##");
                txtSpeed.Tag          = unit.Speed;
                txtSpeed.Text         = ManeuveringBoard.GetSpeedString(unit.Speed, unitSystem);
                cmbType.SelectedIndex = (int)unit.Type;

                if (unit.Parent == null)
                {
                    chkRelative.Enabled = false;
                }
                else
                {
                    chkRelative.Checked = unit.IsMotionRelative;
                }
            }
            else
            {
                txtSpeed.Enabled    = false;
                chkRelative.Enabled = false;
                cmbType.Enabled     = false;

                LineShape line = shape as LineShape;
                if (line != null)
                {
                    double angle = ManeuveringBoard.AngleBetween(line.Start, line.End), length = line.Length;
                    txtDirection.Text = (angle * MathConst.RadiansToDegrees).ToString("0.##");
                    txtDirection.Tag  = angle;
                    txtSize.Text      = ManeuveringBoard.GetDistanceString(length, unitSystem);
                    txtSize.Tag       = length;
                }
                else
                {
                    CircleShape circle = shape as CircleShape;
                    if (circle != null)
                    {
                        txtDirection.Enabled = false;
                        txtSize.Text         = ManeuveringBoard.GetDistanceString(circle.Radius, unitSystem);
                        txtSize.Tag          = circle.Radius;
                        lblSize.Text         = "Radius";
                    }
                    else
                    {
                        throw new NotImplementedException();
                    }
                }
            }

            // set these to false, since they may have been set to true by the programmatic changes above
            directionTextChanged = sizeTextChanged = speedTextChanged = false;
        }
Esempio n. 20
0
        public PositionalDataForm(PositionalDataShape posData, UnitSystem unitSystem) : this()
        {
            this.unitSystem = unitSystem;

            txtTime.Text = ManeuveringBoard.GetTimeString(posData.Time);
            txtTime.Focus();
            txtTime.SelectAll();

            Text = grpObservation.Text = (posData is Waypoint ? "Waypoint" : "Observation") + " Data";

            PositionalDataShape previousPosition = null;
            UnitShape           observer         = posData is Observation ? ((Observation)posData).Observer : null;

            for (int index = posData.Parent.Children.IndexOf(posData) - 1; index >= 0; index--)
            {
                previousPosition = posData.Parent.Children[index] as PositionalDataShape;
                if (previousPosition != null && previousPosition.GetType() == posData.GetType() &&
                    (!(previousPosition is Observation) || ((Observation)previousPosition).Observer == observer))
                {
                    break;
                }
                else
                {
                    previousPosition = null;
                }
            }
            if (previousPosition != null)
            {
                previousTime = previousPosition.Time;
            }

            if (posData is BearingObservation)
            {
                double bearing = ((BearingObservation)posData).Bearing;
                grpPrevious.Enabled           = false;
                grpObserved.Enabled           = false;
                txtObserverDistance.Enabled   = false;
                txtObserverBearing.Text       = (bearing * MathConst.RadiansToDegrees).ToString("0.##");
                txtObserverBearing.Tag        = bearing;
                txtObservedBearing.WasChanged = false; // ignore programmatic change
            }
            else
            {
                this.posDataPoint = posData.Position;
                FillRelativeTo((UnitShape)posData.Parent, txtObservedBearing, txtObservedDistance, out observedPoint);

                if (posData is PointObservation)
                {
                    FillRelativeTo(observer, txtObserverBearing, txtObserverDistance, out observerPoint);
                }
                else
                {
                    grpObserver.Enabled = false;
                    grpObserved.Text    = "Relative to Unit";
                }

                if (previousPosition == null)
                {
                    grpPrevious.Enabled = false;
                }
                else
                {
                    FillRelativeTo(previousPosition, txtPreviousBearing, txtPreviousDistance, out previousPoint);
                }

                waypoint = posData is Waypoint;
                if (waypoint)
                {
                    waypointTimes = posData.Parent.Children.OfType <Waypoint>().Where(w => w != posData).Select(w => w.Time).ToList();
                }
            }
        }
Esempio n. 21
0
        bool UpdateSolution()
        {
            double?speed = null, time = null, aob = null, radius = null;

            if (!string.IsNullOrEmpty(txtSpeed.Text.Trim()))
            {
                double value;
                if (!TryParseSpeed(txtSpeed.Text, unitSystem, out value))
                {
                    ShowInvalidSpeed(txtSpeed.Text);
                    goto invalidData;
                }
                speed = value;
            }

            if (!string.IsNullOrEmpty(txtTime.Text.Trim()))
            {
                TimeSpan timeSpan;
                bool     relative;
                if (!TryParseTime(txtTime.Text, out timeSpan, out relative))
                {
                    ShowInvalidTime(txtTime.Text, false, false);
                    goto invalidData;
                }
                time = timeSpan.TotalSeconds;
            }

            if (!string.IsNullOrEmpty(txtAoB.Text.Trim()))
            {
                double value;
                if (!TryParseAngle(txtAoB.Text.Trim(), out value))
                {
                    ShowInvalidAngle(txtAoB.Text);
                    goto invalidData;
                }
                aob = value;
            }

            if (!string.IsNullOrEmpty(txtRadius.Text.Trim()))
            {
                double value;
                if (!TryParseLength(txtRadius.Text, unitSystem, out value))
                {
                    ShowInvalidLength(txtRadius.Text);
                    goto invalidData;
                }
                if (value != 0)
                {
                    radius = value;
                }
            }

            if (speed.HasValue && time.HasValue)
            {
                lblSolution.Text = "Remove speed or time.";
                return(false);
            }

            if (aob.HasValue && !radius.HasValue)
            {
                lblSolution.Text = "Using AoB requires a radius.";
                return(false);
            }

            if (radius.HasValue && !aob.HasValue)
            {
                lblSolution.Text = "Using a radius requires AoB.";
                return(false);
            }

            Point2  targetPt;
            Vector2 targetVel;
            double  targetCourse;

            if (target != null)
            {
                targetPt     = target.Position;
                targetVel    = target.GetEffectiveVelocity();
                targetCourse = target.Direction;
            }
            else
            {
                double bearing, range;
                if (string.IsNullOrEmpty(txtBearing.Text))
                {
                    lblSolution.Text = "Enter a target bearing.";
                    return(false);
                }
                else if (!TryParseAngle(txtBearing.Text, out bearing))
                {
                    ShowInvalidAngle(txtBearing.Text);
                    goto invalidData;
                }

                if (string.IsNullOrEmpty(txtRange.Text))
                {
                    lblSolution.Text = "Enter a target range.";
                    return(false);
                }
                else if (!TryParseLength(txtRange.Text, unitSystem, out range))
                {
                    ShowInvalidLength(txtRange.Text);
                    goto invalidData;
                }

                targetPt = new Vector2(0, range).Rotate(-bearing).ToPoint();

                double targetSpeed;
                if (string.IsNullOrEmpty(txtTargetSpeed.Text))
                {
                    lblSolution.Text = "Enter a target speed.";
                    return(false);
                }
                else if (!TryParseSpeed(txtTargetSpeed.Text, unitSystem, out targetSpeed))
                {
                    ShowInvalidSpeed(txtTargetSpeed.Text);
                    goto invalidData;
                }

                if (targetSpeed == 0)
                {
                    targetCourse = 0;
                }
                else if (string.IsNullOrEmpty(txtCourse.Text))
                {
                    lblSolution.Text = "Enter a target course.";
                    return(false);
                }
                else if (!TryParseAngle(txtCourse.Text, out targetCourse))
                {
                    ShowInvalidAngle(txtCourse.Text);
                    goto invalidData;
                }

                targetVel = new Vector2(0, targetSpeed).Rotate(-targetCourse);
            }

            // if AoB was specified, then we're actually trying to intercept a single point on the radius circle, so make that are target point
            // and use the standard point intercept algorithm
            if (aob.HasValue)
            {
                targetPt += new Vector2(0, radius.Value).Rotate(-(targetCourse + aob.Value));
            }

            // if we've already satisfied the intercept criteria...
            Vector2 o = unit == null ? new Vector2(targetPt) : targetPt - unit.Position;

            if (o.LengthSqr <= (radius.HasValue && !aob.HasValue ? radius.Value * radius.Value : 0))
            {
                lblSolution.Text = "You are already there.";
                return(false);
            }

            // if the target is not moving, any speed will work, so we'll just arbitrarily head there at 10 units of speed
            if (targetVel.LengthSqr == 0)
            {
                Solution         = o.GetNormal(MB.ConvertFromUnit(10, MB.GetAppropriateSpeedUnit(unitSystem)));
                InterceptPoint   = targetPt;
                lblSolution.Text = ManeuveringBoard.GetAngleString(MB.SwapBearing(o.Angle)) + " (target stationary)";
                return(true);
            }

            // if we have a single target point (i.e. the target itself, or one point on the radius circle), the intercept formula basically
            // consists in solving a quadratic formula. if we're at P and the target is at T with velocity V, then we know that the interception
            // point is at T+V*t, where t is time. if we take the vector from P to the intersection point (T+V*t-P), then the distance is
            // |T+V*t-P|. dividing by the speed s gives us the time: t = |(T+V*t-P)|/s. so s*t = |T+V*t-P|. squaring both sides, replacing T-P
            // with the helper O (i.e. translating P to the origin), and expanding the vector, we get (s*t)^2 = (Ox+Vx*t)^2 + (Oy+Vy*t)^2
            if (!time.HasValue) // if the user didn't specify the time of intercept... (if they did, the problem's solved already)
            {
                if (!speed.HasValue)
                {
                    // if the user specified no information, there are an infinite number of solutions. we'll choose the one that requires
                    // approximately the lowest intercept speed. if we solve the quadratic equation for speed instead of time, we get
                    // s = sqrt((Ox + Vx*t)^2 + (Oy + Vy*t)^2) / t. we need to minimize this equation where s >= 0 and t >= 0. there's probably
                    // some way to do this analytically, but it seems complicated, so we'll just minimize it numerically. we'll represent the time
                    // TODO: check out the inverse of this function. it may be more sloped and easier to optimize?
                    // TODO: consider a better method of rescaling (to get all the numbers about the same magnitude)
                    Func <double, double> speedFunc = t =>
                    {
                        if (t <= 0)
                        {
                            return(double.NaN);
                        }
                        t *= 3600; // we'll scale the time from seconds to hours because the minimizer is a bit more stable when params are around 1.0
                        double x = o.X + targetVel.X * t, y = o.Y + targetVel.Y * t;
                        return(Math.Sqrt(x * x + y * y) / t);
                    };
                    Func <double, double> derivative = t =>
                    {
                        if (t <= 0)
                        {
                            return(double.NaN);
                        }
                        t *= 3600;
                        double x = o.X + targetVel.X * t, y = o.Y + targetVel.Y * t;
                        return(-(o.X * x + o.Y * y) / (t * t * Math.Sqrt(x * x + y * y)));
                    };
                    ConstrainedMinimizer minimizer = new ConstrainedMinimizer(new DifferentiableMDFunction(speedFunc, derivative));
                    // the function tends to be very flat near the minimum, so it's hard to find it exactly. increase the gradient tolerance to
                    // prevent it from failing as often. we're going to be rounding the answer anyway, so it needn't be exact
                    minimizer.GradientTolerance = 1e-6;
                    // constrain the solution to be non-negative
                    minimizer.AddConstraint(new DifferentiableMDFunction(t => - speedFunc(t), t => - derivative(t)));
                    minimizer.SetBounds(0, 0, double.MaxValue); // constrain t to be non-negative
                    try
                    {
                        double[] point    = new double[] { 1 };
                        double   minSpeed = minimizer.Minimize(point);
                        // now we want to round the speed up to the next unit, to make it look nicer. we also want to increase the speed by a small
                        // amount because the time seems to increase without bound as the speed nears the minimum. for instance, a difference of
                        // 0.01 kn near the minimum might increase the time by 5 hours. speeds near the minimum also render the math below very
                        // inaccurate due to mismatching precision. so we'll add about a knot to the speed as well as round it up
                        SpeedUnit speedUnit = MB.GetAppropriateSpeedUnit(unitSystem);
                        speed = MB.ConvertFromUnit(Math.Ceiling(MB.ConvertToUnit(minSpeed + 0.5, speedUnit)), speedUnit); // 0.5 m/s ~= 1 kn
                    }
                    catch (MinimumNotFoundException)
                    {
                        lblSolution.Text = "Try setting parameters.";
                        return(false);
                    }
                }

                // if know the intercept speed, we take the above equation and factor out time. we end up with:
                // t^2(Vx^2 + Vy^2 - s^2) + t*(2Ox*Vx + 2Oy*Vy) + Ox^2 + Oy^2 = 0. if we take A=(Vx^2 + Vy^2 - s^2), B=2(Ox*Vx + Oy*Vy), and
                // C=Ox^2 + Oy^2, then we have the quadratic A*t^2 + B*t + C = 0 which we can solve with the quadratic formula.
                // t = (-B +/- sqrt(B^2 - 4AC)) / 2A (and we can remove a factor of 2). if the discriminant is negative, there is no solution.
                // otherwise, we take whichever solution gives the smallest non-negative time
                double A = targetVel.X * targetVel.X + targetVel.Y * targetVel.Y - speed.Value * speed.Value, B = o.X * targetVel.X + o.Y * targetVel.Y, C = o.X * o.X + o.Y * o.Y;

                // if A = 0, then the speeds are identical, and we get division by zero solving the quadratic. but if A = 0 then we just have
                // B*t + C = 0 or t = -C/B. we know B can't be zero because we checked the relevant cases above
                if (A == 0)
                {
                    double t = -C / (2 * B); // we have to multiply B by 2 because we removed a factor of two above
                    if (t >= 0)
                    {
                        time = t;
                    }
                    else
                    {
                        goto noSolution;
                    }
                }
                else
                {
                    double discriminant = B * B - A * C;
                    if (discriminant < 0)
                    {
                        goto noSolution;
                    }
                    double root = Math.Sqrt(discriminant), time1 = (-B + root) / A, time2 = (-B - root) / A;
                    if (time1 >= 0 && time1 <= time2)
                    {
                        time = time1;
                    }
                    else if (time2 >= 0)
                    {
                        time = time2;
                    }
                    else
                    {
                        goto noSolution;
                    }
                }
            }

            // now that we know the time of intercept, we can calculate the intercept point and get the velocity we'd need to get there.
            // the intercept point is T+V*t. the intercept vector is T+V*t-P = O+V*t. this vector has a length equal to the distance, but we
            // want a length equal to the speed, so divide by time to get speed (e.g. 10 km in 2 hour = 5km/hour). but (O+V*t)/t = O/t+V
            Solution       = o / time.Value + targetVel;
            InterceptPoint = targetPt + targetVel * time.Value;
            haveSolution   = true;

            lblSolution.Text = ManeuveringBoard.GetAngleString(MB.SwapBearing(Solution.Angle)) + " at " +
                               MB.GetSpeedString(Solution.Length, unitSystem) + " for " + GetTimeString(time.Value);
            return(true);

noSolution:
            lblSolution.Text = "No intercept is possible.";
            return(false);

invalidData:
            lblSolution.Text = "Invalid data.";
            return(false);
        }
Esempio n. 22
0
        bool UpdateSolution()
        {
            double?speed = null, time = null, aob = null, radius = null;

            if (!string.IsNullOrEmpty(txtSpeedTDC.Text.Trim()))
            {
                double value;
                if (!TryParseSpeed(txtSpeedTDC.Text, unitSystem, out value))
                {
                    ShowInvalidSpeed(txtSpeedTDC.Text);
                    goto invalidData;
                }
                speed = value;
            }

            Point2  targetPt;
            Vector2 targetVel;
            double  targetCourse;
            double  ownCourse;
            double  targetAOB;


            {
                double bearing, range;
                if (string.IsNullOrEmpty(txtBearingTDC.Text))
                {
                    lblSolutionTDC.Text = "Enter a target true bearing.";
                    return(false);
                }
                else if (!TryParseAngle(txtBearingTDC.Text, out bearing))
                {
                    ShowInvalidAngle(txtBearingTDC.Text);
                    goto invalidData;
                }

                if (string.IsNullOrEmpty(txtRangeTDC.Text))
                {
                    lblSolutionTDC.Text = "Enter a target range.";
                    return(false);
                }
                else if (!TryParseLength(txtRangeTDC.Text, unitSystem, out range))
                {
                    ShowInvalidLength(txtRangeTDC.Text);
                    goto invalidData;
                }

                targetPt = new Vector2(0, range).Rotate(-bearing).ToPoint();

                double targetSpeed;
                if (string.IsNullOrEmpty(txtTargetSpeedTDC.Text))
                {
                    lblSolutionTDC.Text = "Enter a target speed.";
                    return(false);
                }
                else if (!TryParseSpeed(txtTargetSpeedTDC.Text, unitSystem, out targetSpeed))
                {
                    ShowInvalidSpeed(txtTargetSpeedTDC.Text);
                    goto invalidData;
                }



                if (string.IsNullOrEmpty(txtAoBTDC.Text.Trim()))
                {
                    lblSolutionTDC.Text = "Enter target AOB.";
                    return(false);
                }
                else if (!TryParseAngle(txtAoBTDC.Text.Trim(), out targetAOB))
                {
                    ShowInvalidAngle(txtAoBTDC.Text);
                    goto invalidData;
                }

                if (string.IsNullOrEmpty(txtCourseTDC.Text))
                {
                    lblSolutionTDC.Text = "Enter ownship course.";
                    return(false);
                }
                else if (!TryParseAngle(txtCourseTDC.Text, out ownCourse))
                {
                    ShowInvalidAngle(txtCourseTDC.Text);
                    goto invalidData;
                }

                if (targetSpeed == 0)
                {
                    targetCourse = 0;
                }
                else
                {
                    targetCourse = ownCourse + (Math.PI - targetAOB);
                }

                targetVel = new Vector2(0, targetSpeed).Rotate(-targetCourse);
            }

            // if we've already satisfied the intercept criteria...
            Vector2 o = unit == null ? new Vector2(targetPt) : targetPt - unit.Position;

            if (o.LengthSqr <= (radius.HasValue && !aob.HasValue ? radius.Value * radius.Value : 0))
            {
                lblSolutionTDC.Text = "You are already at the target.";
                return(false);
            }

            // if the target is not moving, any speed will work, so we'll just arbitrarily head there at 10 units of speed
            if (targetVel.LengthSqr == 0)
            {
                Solution            = o.GetNormal(MB.ConvertFromUnit(10, MB.GetAppropriateSpeedUnit(unitSystem)));
                InterceptPoint      = targetPt;
                lblSolutionTDC.Text = ManeuveringBoard.GetAngleString(MB.SwapBearing(o.Angle)) + " (target stationary)";
                return(true);
            }

            // if we have a single target point (i.e. the target itself, or one point on the radius circle), the intercept formula basically
            // consists in solving a quadratic formula. if we're at P and the target is at T with velocity V, then we know that the interception
            // point is at T+V*t, where t is time. if we take the vector from P to the intersection point (T+V*t-P), then the distance is
            // |T+V*t-P|. dividing by the speed s gives us the time: t = |(T+V*t-P)|/s. so s*t = |T+V*t-P|. squaring both sides, replacing T-P
            // with the helper O (i.e. translating P to the origin), and expanding the vector, we get (s*t)^2 = (Ox+Vx*t)^2 + (Oy+Vy*t)^2
            if (!time.HasValue) // if the user didn't specify the time of intercept... (if they did, the problem's solved already)
            {
                if (!speed.HasValue)
                {
                    lblSolutionTDC.Text = "Enter torpedo speed.";
                    return(false);
                }

                // if know the intercept speed, we take the above equation and factor out time. we end up with:
                // t^2(Vx^2 + Vy^2 - s^2) + t*(2Ox*Vx + 2Oy*Vy) + Ox^2 + Oy^2 = 0. if we take A=(Vx^2 + Vy^2 - s^2), B=2(Ox*Vx + Oy*Vy), and
                // C=Ox^2 + Oy^2, then we have the quadratic A*t^2 + B*t + C = 0 which we can solve with the quadratic formula.
                // t = (-B +/- sqrt(B^2 - 4AC)) / 2A (and we can remove a factor of 2). if the discriminant is negative, there is no solution.
                // otherwise, we take whichever solution gives the smallest non-negative time
                double A = targetVel.X * targetVel.X + targetVel.Y * targetVel.Y - speed.Value * speed.Value, B = o.X * targetVel.X + o.Y * targetVel.Y, C = o.X * o.X + o.Y * o.Y;

                // if A = 0, then the speeds are identical, and we get division by zero solving the quadratic. but if A = 0 then we just have
                // B*t + C = 0 or t = -C/B. we know B can't be zero because we checked the relevant cases above
                if (A == 0)
                {
                    double t = -C / (2 * B); // we have to multiply B by 2 because we removed a factor of two above
                    if (t >= 0)
                    {
                        time = t;
                    }
                    else
                    {
                        goto noSolution;
                    }
                }
                else
                {
                    double discriminant = B * B - A * C;
                    if (discriminant < 0)
                    {
                        goto noSolution;
                    }
                    double root = Math.Sqrt(discriminant), time1 = (-B + root) / A, time2 = (-B - root) / A;
                    if (time1 >= 0 && time1 <= time2)
                    {
                        time = time1;
                    }
                    else if (time2 >= 0)
                    {
                        time = time2;
                    }
                    else
                    {
                        goto noSolution;
                    }
                }
            }

            // now that we know the time of intercept, we can calculate the intercept point and get the velocity we'd need to get there.
            // the intercept point is T+V*t. the intercept vector is T+V*t-P = O+V*t. this vector has a length equal to the distance, but we
            // want a length equal to the speed, so divide by time to get speed (e.g. 10 km in 2 hour = 5km/hour). but (O+V*t)/t = O/t+V
            Solution       = o / time.Value + targetVel;
            InterceptPoint = targetPt + targetVel * time.Value;
            haveSolution   = true;

            lblSolutionTDC.Text = "Aim at " + ManeuveringBoard.GetAngleString(MB.SwapBearing(Solution.Angle)) + ", torpedo running time " + GetTimeString(time.Value);
            return(true);

noSolution:
            lblSolutionTDC.Text = "No intercept is possible.";
            return(false);

invalidData:
            lblSolutionTDC.Text = "Invalid data.";
            return(false);
        }
Esempio n. 23
0
            public override string GetStatusText(Shape shape)
            {
                UnitShape unit = (UnitShape)shape;

                return(ManeuveringBoard.GetAngleString(unit.Direction) + ", " + unit.Board.GetSpeedString(unit.Speed));
            }