Example #1
0
 /// <summary>
 /// Generates phase lines.
 /// </summary>
 /// <param name="mg">
 /// The MilGraphic associated with the phase lines.
 /// </param>
 /// <param name="anchors">
 /// The map locations of the polyline vertices.
 /// </param>
 /// <param name="points">
 /// The map locations converted to the ResourceDictionary space.
 /// </param>
 /// <returns>
 /// The graphical Path associated with the phase lines.
 /// </returns>
 public static Path GeneratePhaseLine(
     MilGraphic mg,
     IList <ILocation> anchors,
     out IList <Point> points)
 {
     return(MilZone.GenerateLines(mg, false, anchors.Count, anchors, out points));
 }
Example #2
0
        /// <summary>
        /// AddArrow generates the missing points for an arrow's tail as appropriate for Bing's mercator projection
        /// </summary>
        /// <param name="mg">
        /// The MilGraphic representing the arrow shape.
        /// </param>
        /// <param name="inPoints">
        /// The points from the resource dictionary entry for the particular symbol.
        /// </param>
        /// <param name="inAnchors">
        /// The points representing the map coordinates of the arrow's head to tail points.
        /// </param>
        /// <returns>
        /// A list of points that represent the head to tail coordinates of the arrow
        /// </returns>
        public static IList <Point> AddArrow(
            MilGraphic mg, IList <Point> inPoints, IList <ILocation> inAnchors)
        {
            ////                  ·N-1
            ////                  |\
            ////      ------------  \ 
            ////      ·1             ·0
            ////      ------------  /
            ////                  |/

            // First point [0] is the head of the arrow.
            // Second point [1] is the end of the first arrow segment.
            // Subsequent points define additional arrow segment ends.
            // Last point [N - 1] is the top of the arrowhead (different from 2525B).
            // Looks like the height of the arrow body is 1/2 of the height of the arrow head.

            // Convert the points into "pixel" space. To keep a constant arrow
            // width it makes more sense to do the computations in this space.
            var           count  = mg.Anchors.Count;
            IList <Point> points = MapHelper.LatLonsToPixels(mg.Anchors, 4.0);

            // This is the projection of the arrowhead onto the arrow baseline
            var pt = MapHelper.CalculateProjection(points[0], points[1], points[count - 1]);

            // The origin is the first point in this points array
            var origin = points[0];

            // Compute half the width of the arrow
            var distance = 0.5 * PointHelper.Magnitude(pt, points[count - 1]);

            // Let's go ahead and compute the unit vectors in the direction
            // of each vector making up the arrow, starting at the head and
            // moving towards the tail.
            var unitVectors = new Point[count - 2];

            for (int i = 0; i < count - 2; i++)
            {
                unitVectors[i] = MapHelper.Normalize(points[i], points[i + 1]);
            }

            var leftNormal  = new Point[count - 2];
            var rightNormal = new Point[count - 2];

            // Now compute the "left" and "right" perpendicular unit vectors
            Point normal;

            for (int i = 0; i < count - 2; i++)
            {
                // The rightNormal vectors cross with the unit vector to give a +1 in Z
                // The leftNormal vectors cross with the unit vector to give a -1 in Z
                normal = new Point(unitVectors[i].Y, -unitVectors[i].X);
                var cross = (normal.X * unitVectors[i].Y) - (normal.Y * unitVectors[i].X);
                leftNormal[i]  = (cross < 0.0) ? normal : new Point(-normal.X, -normal.Y);
                rightNormal[i] = (cross < 0.0) ? new Point(-normal.X, -normal.Y) : normal;
            }

            // We're not mitering any of these angles - that's up to the user.
            // So we're bisecting the angle between two vectors and moving
            // out a distance based on the tangent of that angle.
            // This gives us the outside point.
            // So outsidePoint = point[i] + d * outsideVector[i-1] + d / tan(<) * unitVector[i-1]
            // where < is acos(dot(unitVector[i-1], unitVector[i])).
            //
            //                         ______. d * tan(<)
            //                        d|<
            //        i-1------->-------+i
            //                          \
            //                           \
            //                             i+1

            // Need to flip the starting points if Aviation, Airborne or RotaryWing
            bool flip = mg.StencilType == "AxisOfAdvanceAviation" ||
                        mg.StencilType == "AxisOfAdvanceAirborne" ||
                        mg.StencilType == "AxisOfAdvanceRotaryWing";
            var fwd = PointHelper.LinearCombination(pt, distance, leftNormal[0]);
            var bck = PointHelper.LinearCombination(pt, distance, rightNormal[0]);

            // Start the arrow's body - first point defined by the top of the arrowhead
            var arrow = new List <Point> {
                flip?bck : fwd
            };
            var backArrow = new List <Point> {
                flip?fwd : bck
            };

            for (int i = 0; i < count - 2; i++)
            {
                // Get bisected angle
                // Use the half-angle tangent formula here instead of the brute force method
                // var angle = Math.Atan2(
                //    unitVectors[i].Y * unitVectors[i + 1].X - unitVectors[i].X * unitVectors[i + 1].Y,
                //    unitVectors[i].X * unitVectors[i + 1].X + unitVectors[i].Y * unitVectors[i + 1].Y) / 2.0;
                // var tangent = Math.Tan(angle);
                var tangent = (i == count - 3) ? 0.0 :
                              ((unitVectors[i].Y * unitVectors[i + 1].X) - (unitVectors[i].X * unitVectors[i + 1].Y)) /
                              ((unitVectors[i].X * unitVectors[i + 1].X) + (unitVectors[i].Y * unitVectors[i + 1].Y) + 1.0);

                // points[i+1] + distance * (leftNormal[i] + tangent * unitVectors[i])
                arrow.Add(
                    PointHelper.LinearCombination(
                        points[i + 1], distance, PointHelper.LinearCombination(leftNormal[i], tangent, unitVectors[i])));

                // points[i+1] + distance * (rightNormal[i] - tangent * unitVectors[i])
                backArrow.Add(
                    PointHelper.LinearCombination(
                        points[i + 1], distance, PointHelper.LinearCombination(rightNormal[i], -tangent, unitVectors[i])));
            }

            points.Clear();

            count = backArrow.Count;
            for (int i = count - 1; i >= 0; i--)
            {
                arrow.Add(backArrow[i]);
            }

            // Add the origin and the projected arrowhead point to the list of arrow points.
            // We need to get these points transformed and this
            // is currently the only way to do it.
            // We'll have to remove them later from the list.
            arrow.Add(origin);
            arrow.Add(pt);

            mg.IsSpline = false;
            IList <ILocation> anchors = arrow.Select(a => MapHelper.PixelToLatLon(a, 4.0)).ToList();

            Path path = MilZone.GenerateLines(mg, false, anchors.Count - 2, anchors, out points);

            // OK, let's record the transformed origin and kill these extra points.
            int index = points.Count - 1;

            pt = points[index];
            points.RemoveAt(index);
            anchors.RemoveAt(index);
            index--;
            origin = points[index];
            points.RemoveAt(index);
            anchors.RemoveAt(index);

            double newScaleFactor;

            MilGraphic.FullTransformation(mg, path, null, points, anchors, origin, out newScaleFactor);

            // We also need the oldScaleFactor - the scaleFactor that the rest of the graphic will be using.
            double oldScaleFactor;

            MilGraphic.ComputeScales(MapHelper.ComputeTransform(inPoints, inAnchors), out oldScaleFactor);

            // OK now we need to scale the transform matrix relative to the oldScaleFactor
            var r = newScaleFactor / oldScaleFactor;

            SetPathMatrix(r, path);
            mg.AddChild("Boundary", path);
            if (mg.StencilType == "AxisOfAdvanceForFeint")
            {
                // We need to add LabelT but the position (right after the projection
                // of the arrow head onto the main arrow axis) needs to be scaled according
                // to our transformation which uses the "r" scale factor.
                var pointNew = PointHelper.LinearCombination(origin, r, PointHelper.Minus(pt, origin));
                mg.AddChild(
                    "Label",
                    MilLine.GenerateLabels(mg, new[] { mg.LabelT }, pointNew, origin, origin, new Point(1.0, 0.5)));
            }
            else if (mg.StencilType == "AxisOfAdvanceRotaryWing")
            {
                // Add rotary wing symbol by appending to existing path
                AddRotaryWing(path, points);
            }

            return(points);
        }