private static Geometry ProcessGeometryHorizontalTraverse(GunPitchLimitsComponent traverse,
                                                                  TurretYawLimits yawLimits,
                                                                  Point center,
                                                                  double geometryRotation,
                                                                  Geometry geometry,
                                                                  double maxRadius)
        {
            if (yawLimits == null || yawLimits.Range == 360)
            {
                return(geometry);
            }
            else
            {
                // create a horizontal fan
                var fanFigure = new PathFigure {
                    StartPoint = center
                };
                var point = GunTraverseHelper.PolarToCartesian(center, maxRadius, 360 - yawLimits.Right + geometryRotation);
                fanFigure.Segments.Add(new LineSegment(point, true));
                point = GunTraverseHelper.PolarToCartesian(center, maxRadius, -yawLimits.Left + geometryRotation);
                fanFigure.Segments.Add(new ArcSegment(point,
                                                      new Size(maxRadius, maxRadius),
                                                      yawLimits.Range,
                                                      yawLimits.Range > 180,
                                                      SweepDirection.Counterclockwise,
                                                      true));
                fanFigure.IsClosed = true;
                var fanGeometry = new PathGeometry();
                fanGeometry.Figures.Add(fanFigure);

                return(new CombinedGeometry(GeometryCombineMode.Intersect, geometry, fanGeometry));
            }
        }
        public static Geometry CreateGeometry(GunPitchLimitsComponent traverse,
                                              TurretYawLimits yawLimits,
                                              double size,
                                              Point center,
                                              double geometryRotation = 0,
                                              double margin           = 0,
                                              double padding          = 0,
                                              Func <double, double> verticalTraverseTransform = null)
        {
            if (verticalTraverseTransform == null)
            {
                verticalTraverseTransform = GunTraverseHelper.DefaultVerticalTraverseTransform;
            }

            if (traverse.HasSingularValue && yawLimits.Range == 360)
            {
                var radius = size / 2 - margin;
                return(new EllipseGeometry(center, radius, radius));
            }
            else
            {
                var maxRadiusInDegrees = traverse.GetMaxValue();
                var scale = (size / 2 - margin - padding) / maxRadiusInDegrees;

                Func <double, double> radiusConverter = r => padding + Math.Max(verticalTraverseTransform(r) * scale, 0);

                return(GunTraverseHelper.CreateGeometry(traverse, yawLimits, center, radiusConverter, geometryRotation));
            }
        }
        public void SetTraverse(GunPitchLimitsComponent gunPitch, TurretYawLimits turretYaw)
        {
            this.CurveCanvas.Children.Clear();

            if (gunPitch == null)
            {
                return;
            }

            var traverseFigureStrokeStyle = this.FindResource("TraverseFigure") as Style;

            var maxRadius = gunPitch.GetMaxValue();
            //maxRadius = Math.Ceiling(maxRadius / 5) * 5;
            const double margin = 1;

            var scale      = 80 / (maxRadius + margin);
            var size       = scale * maxRadius * 2;
            var center     = new Point(100, 100);
            var figurePath = new Path
            {
                Data = GunTraverseHelper.CreateGeometry(gunPitch,
                                                        turretYaw,
                                                        size,
                                                        center,
                                                        90,
                                                        scale * margin,
                                                        verticalTraverseTransform: v => v + 1),
                Style = traverseFigureStrokeStyle
            };

            this.CurveCanvas.Children.Add(figurePath);

            // draw references
            var thinReferenceGeometry  = new GeometryGroup();
            var thickReferenceGeometry = new GeometryGroup();

            const double referenceCircleGap     = 20;
            const double halfReferenceCircleGap = referenceCircleGap / 2;

            var sign         = Math.Sign(maxRadius);
            var absMaxRadius = Math.Abs(maxRadius);

            var thickDivisor = absMaxRadius > 45 ? 20 : absMaxRadius > 25 ? 10 : 5;
            var thinDivisor  = absMaxRadius > 25 ? 5 : 1;

            for (var i = thinDivisor; i <= absMaxRadius; i += thinDivisor)
            {
                var radius = scale * i * sign;

                Geometry geometry;

                var isThickRing = i % thickDivisor == 0;
                if (absMaxRadius < 10 || isThickRing)
                {
                    var gapHalfAngle = Math.Asin(halfReferenceCircleGap / radius) * 180 / Math.PI;
                    if (double.IsNaN(gapHalfAngle))
                    {
                        gapHalfAngle = 180;
                    }
                    var startPoint = GunTraverseHelper.PolarToCartesian(center, radius, gapHalfAngle);
                    var endPoint   = GunTraverseHelper.PolarToCartesian(center, radius, 360 - gapHalfAngle);
                    var arc        = new ArcSegment(endPoint, new Size(radius, radius), 360 - gapHalfAngle * 2, true, SweepDirection.Counterclockwise, true);
                    var figure     = new PathFigure(startPoint, new[] { arc }, false);
                    geometry = new PathGeometry(new[] { figure });


                    var referenceTextContainer = new Grid
                    {
                        Width  = 20,
                        Height = referenceCircleGap
                    };
                    var referenceTextContainerPosition = GunTraverseHelper.PolarToCartesian(center, radius, 0);
                    Canvas.SetLeft(referenceTextContainer, referenceTextContainerPosition.X - 10);
                    Canvas.SetTop(referenceTextContainer, referenceTextContainerPosition.Y - halfReferenceCircleGap);

                    var referenceText = new TextBlock
                    {
                        Text = (i * sign).ToString(),
                        HorizontalAlignment = HorizontalAlignment.Center,
                        VerticalAlignment   = VerticalAlignment.Center,
                        FontSize            = isThickRing ? 10 : 8,
                        Foreground          = isThickRing ? Brushes.White : new SolidColorBrush(Color.FromArgb(0x80, 0xff, 0xff, 0xff))
                    };


                    referenceTextContainer.Children.Add(referenceText);

                    this.CurveCanvas.Children.Add(referenceTextContainer);
                }
                else
                {
                    geometry = new EllipseGeometry(center, radius, radius);
                }

                if (isThickRing)
                {
                    thickReferenceGeometry.Children.Add(geometry);
                }
                else
                {
                    thinReferenceGeometry.Children.Add(geometry);
                }
            }

            var thinReferencePath = new Path
            {
                Data  = thinReferenceGeometry,
                Style = this.FindResource("ThinReferenceStroke") as Style
            };

            this.CurveCanvas.Children.Add(thinReferencePath);

            var thickReferencePath = new Path
            {
                Data  = thickReferenceGeometry,
                Style = this.FindResource("ThickReferenceStroke") as Style
            };

            this.CurveCanvas.Children.Add(thickReferencePath);
        }
        public static Geometry CreateGeometry(GunPitchLimitsComponent traverse,
                                              TurretYawLimits yawLimits,
                                              Point center,
                                              Func <double, double> radiusConverter,
                                              double geometryRotation = 0)
        {
            Geometry geometry;
            double   maxRadius;
            var      data = traverse.Data;

            if (traverse.HasSingularValue)
            {
                var radius = radiusConverter(data[0].Limit);
                geometry  = new EllipseGeometry(center, radius, radius);
                maxRadius = radius;
            }
            else
            {
                maxRadius = radiusConverter(traverse.GetMaxValue());

                var figureFigure = new PathFigure();

                var previousTheta  = 0.0;
                var previousRadius = 0.0;
                var previousNode   = (GunPitchLimitsComponent.PitchData)null;

                for (var i = 0; i < data.Length; ++i)
                {
                    var node       = data[i];
                    var theta      = geometryRotation + node.Angle * 360;
                    var radius     = radiusConverter(node.Limit);
                    var point      = GunTraverseHelper.PolarToCartesian(center, radius, theta);
                    var thetaRange = theta - previousTheta;

                    if (i == 0)
                    {
                        figureFigure.StartPoint = point;
                    }
                    else
                    {
                        Debug.Assert(previousNode != null, "previousNode != null");

                        if (previousNode.Angle == node.Angle)
                        {
                            figureFigure.Segments.Add(new LineSegment(point, true));
                        }
                        else if (previousNode.Limit == node.Limit)
                        {
                            figureFigure.Segments.Add(new ArcSegment(point,
                                                                     new Size(radius, radius),
                                                                     thetaRange,
                                                                     thetaRange >= 180,
                                                                     SweepDirection.Counterclockwise,
                                                                     true));
                        }
                        else
                        {
                            figureFigure.Segments.Add(GunTraverseHelper.CreateTraverseFigureTransition(ref figureFigure,
                                                                                                       ref center,
                                                                                                       previousTheta,
                                                                                                       thetaRange,
                                                                                                       previousRadius,
                                                                                                       radius));
                        }
                    }

                    previousTheta  = theta;
                    previousNode   = node;
                    previousRadius = radius;
                }


                var pathGeometry = new PathGeometry();
                pathGeometry.Figures.Add(figureFigure);
                geometry = pathGeometry;
            }

            return(GunTraverseHelper.ProcessGeometryHorizontalTraverse(traverse,
                                                                       yawLimits,
                                                                       center,
                                                                       geometryRotation,
                                                                       geometry,
                                                                       maxRadius));
        }