예제 #1
0
        public static void DrawText(this DrawingContext drawingContext, string text, Point point, Typeface typeface, double emSize,
                                    Color color, Color?haloColor = null, double?haloRadius = null, double angle = 0)
        {
            var formattedText = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                                                  typeface, emSize, new SolidColorBrush(color));

            var width  = formattedText.Width + haloRadius.GetValueOrDefault() * 2;
            var height = formattedText.Height + haloRadius.GetValueOrDefault() * 2;

            var rotate  = new RotateTransform(angle);
            var newRect = rotate.TransformBounds(new Rect(new Point(0, 0), new Size(width, height)));

            drawingContext.PushTransform(new TranslateTransform(-newRect.Left, -newRect.Top));
            drawingContext.PushTransform(rotate);

            if (haloColor.HasValue && haloRadius.HasValue && haloRadius.Value > 0)
            {
                var pen          = new Pen(new SolidColorBrush(haloColor.Value), haloRadius.Value * 2);
                var textGeometry = formattedText.BuildGeometry(point);
                drawingContext.DrawGeometry(null, pen, textGeometry);
            }
            drawingContext.DrawText(formattedText, point);

            drawingContext.Pop();
            drawingContext.Pop();
        }
예제 #2
0
        internal void RotateImage(double degrees)
        {
            ImageSource source = image.Source;

            // then copy the image to a format we can handle.
            double w = source.Width;
            double h = source.Height;

            RenderTargetBitmap copy = new RenderTargetBitmap((int)h, (int)w, 96, 96, PixelFormats.Pbgra32);
            Image img = new Image();

            img.Source = source;
            img.Width  = w;
            img.Height = h;
            // rotate the angle about 0,0
            RotateTransform rotate = new RotateTransform(degrees, 0, 0);

            Rect bounds = new Rect(0, 0, w, h);
            // figure out where the bounds ends up with this rotation.
            Rect transformed = rotate.TransformBounds(bounds);

            // keep the bounds in non-negative territory by adjusting with a translate.
            TransformGroup group = new TransformGroup();

            group.Children.Add(rotate);
            group.Children.Add(new TranslateTransform(transformed.X < 0 ? -transformed.X : 0,
                                                      transformed.Y < 0 ? -transformed.Y : 0));

            img.RenderTransform = group;
            img.Arrange(bounds);
            copy.Render(img);

            image.Source = copy;
        }
        private void ComputeVideoBruchTransform()
        {
            if (this.captureDevice == null)
            {
                return;
            }

            var tmptransform = new RotateTransform {
                Angle = this.orientationAngle + this.captureDevice.SensorRotationInDegrees
            };
            var previewSize = tmptransform.TransformBounds(new Rect(new Point(), new System.Windows.Size(this.captureDevice.PreviewResolution.Width, this.captureDevice.PreviewResolution.Height)));

            double s1 = this.viewfinderCanvas.ActualWidth / previewSize.Width;
            double s2 = this.viewfinderCanvas.ActualHeight / previewSize.Height;

            double scale = Math.Max(s1, s2);

            var t = new TransformGroup();

            if (this.sensorLocation == CameraSensorLocation.Front)
            {
                t.Children.Add(new CompositeTransform
                {
                    Rotation = -(this.orientationAngle + this.captureDevice.SensorRotationInDegrees),
                    CenterX  = this.viewfinderCanvas.ActualWidth / 2,
                    CenterY  = this.viewfinderCanvas.ActualHeight / 2,
                    ScaleX   = scale,
                    ScaleY   = scale
                });

                t.Children.Add(new ScaleTransform
                {
                    ScaleX  = -1,
                    CenterX = this.viewfinderCanvas.ActualWidth / 2,
                    CenterY = this.viewfinderCanvas.ActualHeight / 2
                });
            }
            else
            {
                t.Children.Add(
                    new CompositeTransform
                {
                    Rotation = this.orientationAngle + this.captureDevice.SensorRotationInDegrees,
                    CenterX  = this.viewfinderCanvas.ActualWidth / 2,
                    CenterY  = this.viewfinderCanvas.ActualHeight / 2,
                    ScaleX   = scale,
                    ScaleY   = scale
                });
            }

            this.viewfinderBrush.Transform = t;
        }
예제 #4
0
        public void TransformBounds()
        {
            var rect      = new Rect(-5, -5, 25, 10);
            var transform = new RotateTransform {
                Angle = 45
            };
            var result = transform.TransformBounds(rect);

            result.X.Should().BeInRange(-7.071068, -7.071067);
            result.Y.Should().BeInRange(-7.071068, -7.071067);
            result.Height.Should().BeInRange(24.748737, 24.748738);
            result.Width.Should().BeInRange(24.748737, 24.748738);
        }
예제 #5
0
        public override bool ShapeAllInRect(Rectangle rect)
        {
            PreviewPoint p1 = PointToZoomPoint(new PreviewPoint(rect.X, rect.Y));
            PreviewPoint p2 = PointToZoomPoint(new PreviewPoint(rect.X + rect.Width, rect.Y + rect.Height));
            int          X1 = Math.Min(p1.X, p2.X);
            int          X2 = Math.Max(p1.X, p2.X);
            int          Y1 = Math.Min(p1.Y, p2.Y);
            int          Y2 = Math.Max(p1.Y, p2.Y);

            var rt = new RotateTransform(RotationAngle, _rotationCenter.X, _rotationCenter.Y);
            var b  = rt.TransformBounds(Bounds);

            return(b.Top >= Y1 && b.Bottom <= Y2 && b.Left >= X1 && b.Right <= X2);
        }
예제 #6
0
        /// <summary>
        /// This function calculates the smallest axis-aligned bounding box possible for the current ViewPort.
        /// This means that if the current Map has a Heading that is not a multiple of 90°
        /// this function will a bounding box that is bigger than the actual ViewPort.
        /// </summary>
        public virtual Rect GetViewportBounds()
        {
            double zoomFactor = ViewPortProjection.GetZoomFactor(ZoomLevel);
            double halfHeight = RenderSize.Height / (2 * zoomFactor);
            double halfWidth  = RenderSize.Width / (2 * zoomFactor);

            Point topLeft     = new Point(ViewPortCenter.X - halfWidth, ViewPortCenter.Y - halfHeight);
            Point bottomRight = new Point(ViewPortCenter.X + halfWidth, ViewPortCenter.Y + halfHeight);

            RotateTransform rotation = new RotateTransform {
                Angle = Heading, CenterY = ViewPortCenter.Y, CenterX = ViewPortCenter.X
            };

            Rect rect = new Rect(topLeft, bottomRight);

            rect = rotation.TransformBounds(rect);
            return(rect);
        }
예제 #7
0
        public WordDrawing(WordCloudEntry wordEntry, WordCloudTheme theme, DpiScale scale)
        {
            var text = new FormattedText(wordEntry.Word,
                                         CultureInfo.CurrentUICulture,
                                         FlowDirection.LeftToRight,
                                         theme.Typeface,
                                         100,
                                         wordEntry.Brush,
                                         scale.PixelsPerDip);

            var textGeometry = text.BuildGeometry(new Point(0, 0));

            _geo           = textGeometry;
            WordCloudEntry = wordEntry;
            _bounds        = textGeometry.Bounds;
            _geo.Transform = _transformGroup;

            var rotateTransform = new RotateTransform(WordCloudEntry.Angle, _bounds.Width / 2, _bounds.Height / 2);

            _transformGroup.Children.Add(rotateTransform);
            _bounds = rotateTransform.TransformBounds(_bounds);

            _transformGroup.Children.Add(_scaleTransformGroup);

            _initialPlacementTransform = new TranslateTransform(-_bounds.X, -_bounds.Y);


            _transformGroup.Children.Add(_initialPlacementTransform);

            _bounds.X = 0;
            _bounds.Y = 0;

            IntWidth  = (int)Math.Ceiling(_bounds.Width);
            IntHeight = (int)Math.Ceiling(_bounds.Height);
            _transformGroup.Children.Add(_translateTransform);
        }
예제 #8
0
        private static Point DrawLineArrowInternal(DrawingContext dc, double half, Pen pen, Brush brush, double x, double y, double angle, ArrowStyle style)
        {
            Point  pt;
            bool   doRectTransform = angle % 90.0 != 0.0;
            var    rt = new RotateTransform(angle, x, y);
            double rx = style.RadiusX;
            double ry = style.RadiusY;
            double sx = 2.0 * rx;
            double sy = 2.0 * ry;

            switch (style.ArrowType)
            {
            default:
            case ArrowType.None:
            {
                pt = new Point(x, y);
            }
            break;

            case ArrowType.Rectangle:
            {
                pt = rt.Transform(new Point(x - sx, y));
                var rect = new Rect(x - sx, y - ry, sx, sy);
                if (doRectTransform)
                {
                    dc.PushTransform(rt);
                    DrawRectangleInternal(dc, half, brush, pen, style.IsStroked, style.IsFilled, ref rect);
                    dc.Pop();
                }
                else
                {
                    var bounds = rt.TransformBounds(rect);
                    DrawRectangleInternal(dc, half, brush, pen, style.IsStroked, style.IsFilled, ref bounds);
                }
            }
            break;

            case ArrowType.Ellipse:
            {
                pt = rt.Transform(new Point(x - sx, y));
                dc.PushTransform(rt);
                var c = new Point(x - rx, y);
                DrawEllipseInternal(dc, half, brush, pen, style.IsStroked, style.IsFilled, ref c, rx, ry);
                dc.Pop();
            }
            break;

            case ArrowType.Arrow:
            {
                pt = rt.Transform(new Point(x, y));
                var p11 = rt.Transform(new Point(x - sx, y + sy));
                var p21 = rt.Transform(new Point(x, y));
                var p12 = rt.Transform(new Point(x - sx, y - sy));
                var p22 = rt.Transform(new Point(x, y));
                DrawLineInternal(dc, half, pen, style.IsStroked, ref p11, ref p21);
                DrawLineInternal(dc, half, pen, style.IsStroked, ref p12, ref p22);
            }
            break;
            }

            return(pt);
        }
예제 #9
0
        private void RotateTransform_Changed(object sender, EventArgs e)
        {
            Rect newRect = _rotateTransform.TransformBounds(_bounds);

            NotifyShapeChanged(ref _bounds, ref newRect);
        }
예제 #10
0
        // Draw rotated text at the indicated location.
        public static void DrawRotatedString(this DrawingContext drawing_context,
                                             string text, double angle, string font_name, double em_size, Brush brush,
                                             Point origin, TextAlignment text_align, VertAlignment valign, TextAlignment halign)
        {
            Typeface      typeface       = new Typeface(font_name);
            FormattedText formatted_text = new FormattedText(
                text, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight,
                typeface, em_size, brush);

            formatted_text.TextAlignment = text_align;

            // Make a transformation to center the text.
            double             width      = formatted_text.Width - formatted_text.OverhangLeading;
            double             height     = formatted_text.Height;
            TranslateTransform translate1 = new TranslateTransform();

            translate1.Y = -height / 2;
            if ((text_align == TextAlignment.Left) ||
                (text_align == TextAlignment.Justify))
            {
                translate1.X = -width / 2;
            }
            else if (text_align == TextAlignment.Right)
            {
                translate1.X = width / 2;
            }
            else
            {
                translate1.X = 0;
            }

            // Make a transformation to rotate the text.
            RotateTransform rotate = new RotateTransform(angle);

            // Get the text's bounding rectangle.
            Rect rect = new Rect(0, 0, width, height);

            if (text_align == TextAlignment.Center)
            {
                rect.X -= width / 2;
            }
            else if (text_align == TextAlignment.Right)
            {
                rect.X -= width;
            }

            // Get the rotated bounding rectangle.
            Rect rotated_rect = rotate.TransformBounds(rect);

            // Make a transformation to center the
            // bounding rectangle at the destination.
            TranslateTransform translate2 = new TranslateTransform(origin.X, origin.Y);

            // Adjust the translation for the desired alignment.
            if (halign == TextAlignment.Left)
            {
                translate2.X += rotated_rect.Width / 2;
            }
            else if (halign == TextAlignment.Right)
            {
                translate2.X -= rotated_rect.Width / 2;
            }
            if (valign == VertAlignment.Top)
            {
                translate2.Y += rotated_rect.Height / 2;
            }
            else if (valign == VertAlignment.Bottom)
            {
                translate2.Y -= rotated_rect.Height / 2;
            }

            // Push transformations in reverse order.
            drawing_context.PushTransform(translate2);
            drawing_context.PushTransform(rotate);
            drawing_context.PushTransform(translate1);

            // Draw.
            drawing_context.DrawText(formatted_text, new Point(0, 0));

            // Draw a rectangle around the text. (For debugging.)
            drawing_context.DrawRectangle(null, new Pen(Brushes.Red, 1), rect);

            // Remove the transformations.
            drawing_context.Pop();
            drawing_context.Pop();
            drawing_context.Pop();

            // Draw the rotated bounding rectangle. (For debugging.)
            Rect transformed_rect =
                translate2.TransformBounds(
                    rotate.TransformBounds(
                        translate1.TransformBounds(rect)));
            Pen custom_pen = new Pen(Brushes.Blue, 1);

            custom_pen.DashStyle = new DashStyle(
                new double[] { 5, 5 }, 0);
            drawing_context.DrawRectangle(null, custom_pen, transformed_rect);
        }
예제 #11
0
        private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            ContentControl designerItem = DataContext as ContentControl;

            if (designerItem != null)
            {
                Point dragDelta = new Point(e.HorizontalChange, e.VerticalChange);

                RotateTransform rotateTransform = designerItem.RenderTransform as RotateTransform;
                if (rotateTransform != null)
                {
                    dragDelta = rotateTransform.Transform(dragDelta);
                }

                Canvas parent_canvas = designerItem.Parent as Canvas;

                double left_canvas   = Canvas.GetLeft(designerItem);
                double right_canvas  = left_canvas + designerItem.ActualWidth;
                double top_canvas    = Canvas.GetTop(designerItem);
                double bottom_canvas = top_canvas + designerItem.ActualHeight;

                Rect orig_bounds = new Rect(designerItem.RenderSize);
                Rect bounds      = new Rect(designerItem.RenderSize);

                if (rotateTransform != null)
                {
                    bounds = rotateTransform.TransformBounds(bounds);
                }

                if (rotateTransform != null)
                {
                    left_canvas = left_canvas + (orig_bounds.Width - bounds.Width) / 2.0;
                    top_canvas  = top_canvas + (orig_bounds.Height - bounds.Height) / 2.0;

                    Canvas.SetLeft(boundingBox, left_canvas);
                    Canvas.SetTop(boundingBox, top_canvas);
                    boundingBox.Width  = bounds.Width;
                    boundingBox.Height = bounds.Height;
                    if (!added)
                    {
                        (designerItem.Parent as Canvas).Children.Add(boundingBox);
                        added = true;
                    }
                    else
                    {
                        boundingBox.UpdateLayout();
                    }
                }


                //Horizontal Colision
                if (left_canvas + dragDelta.X < 0)
                {
                    Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem));
                }
                else if (left_canvas + Math.Abs(bounds.Width) + dragDelta.X > parent_canvas.ActualWidth)
                {
                    Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem));
                }
                else
                {
                    Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) + dragDelta.X);
                }

                //Vertical Collision
                if (top_canvas + dragDelta.Y < 0)
                {
                    Canvas.SetTop(designerItem, Canvas.GetTop(designerItem));
                }
                else if (top_canvas + Math.Abs(bounds.Height) + dragDelta.Y > parent_canvas.ActualHeight + 5)
                {
                    Canvas.SetTop(designerItem, Canvas.GetTop(designerItem));
                }
                else
                {
                    Canvas.SetTop(designerItem, Canvas.GetTop(designerItem) + dragDelta.Y);
                }
            }
        }
예제 #12
0
        private void RotateThumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            //Initialize the selected widgets' context when first rotate
            if (_infoItems.Count <= 0)
            {
                WidgetViewModBase wdg = designerItem.DataContext as WidgetViewModBase;
                if (wdg.IsGroup == false)
                {
                    wdg.CreateNewPropertyMementos();
                    wdg.PropertyMementos.AddPropertyMemento(new PropertyMemento("RotateAngle", wdg.RotateAngle, wdg.RotateAngle));
                    _infoItems.Add(wdg);
                }
                else
                {
                    foreach (WidgetViewModBase item in (wdg as GroupViewModel).WidgetChildren)
                    {
                        item.CreateNewPropertyMementos();

                        item.CreateNewPropertyMementos();

                        item.PropertyMementos.AddPropertyMemento(new PropertyMemento("RotateAngle", item.RotateAngle, item.RotateAngle));
                        item.PropertyMementos.AddPropertyMemento(new PropertyMemento("Left", item.Raw_Left, item.Raw_Left));
                        item.PropertyMementos.AddPropertyMemento(new PropertyMemento("Top", item.Raw_Top, item.Raw_Top));
                        _infoItems.Add(item);
                    }
                }
            }

            //Rotate the current widget
            if (this.designerItem != null && this.canvas != null)
            {
                ContentPresenter wrapper = VisualTreeHelper.GetParent(VisualTreeHelper.GetParent(designerItem)) as ContentPresenter;

                Point  currentPoint = Mouse.GetPosition(this.canvas);
                Vector deltaVector  = Point.Subtract(currentPoint, this.centerPoint);

                double angle = Vector.AngleBetween(this.startVector, deltaVector);

                RotateTransform rotateTransform = designerItem.RenderTransform as RotateTransform;
                angle = this.initialAngle + Math.Round(angle, 0);



                if (angle < 0)
                {
                    angle += 360;
                }
                else if (angle >= 360)
                {
                    angle -= 360;
                }


                wrapper.InvalidateMeasure();

                if (this.designerItem.IsGroup == true)
                {
                    GroupViewModel group = this.designerItem.DataContext as GroupViewModel;

                    DesignerCanvas dc = canvas as DesignerCanvas;

                    foreach (WidgetViewModBase item in group.WidgetChildren)
                    {
                        RotateTransform rt = new RotateTransform(); // bw.RenderTransform as RotateTransform;
                        rt.Angle   = angle - groupIntialAngle;
                        rt.CenterX = groupCenterX;
                        rt.CenterY = groupCenterY;

                        double oldAngle = item.RotateAngle;
                        item.RotateAngle = Convert.ToInt16(angle - groupIntialAngle);
                        Rect rect1 = rt.TransformBounds(new Rect(item.Left, item.Top, item.ItemWidth, item.ItemHeight));
                        Rect rect2 = item.RevertBoundingRectangle(rect1);

                        //item.IsActual = true;
                        item.Raw_Left = rect2.Left;
                        item.Raw_Top  = rect2.Top;

                        //only widget can rotate
                        if (item is WidgetRotateViewModBase)
                        {
                            int newAngle = Convert.ToInt16(oldAngle + (angle - groupIntialAngle)) % 360;
                            if (newAngle < 0)
                            {
                                newAngle += 360;
                            }
                            else if (angle >= 360)
                            {
                                newAngle -= 360;
                            }
                            item.RotateAngle = newAngle;
                        }
                        else
                        {
                            item.RotateAngle = 0;
                        }
                    }

                    group.Refresh();
                    groupIntialAngle = angle;
                }
                else
                {
                    rotateTransform.Angle = angle;
                }


                if (this.designerItem.ParentID != Guid.Empty)
                {
                    IGroupOperation pageVM = canvas.DataContext as IGroupOperation;
                    pageVM.UpdateGroup(this.designerItem.ParentID);
                }

                if (this.designerItem.IsGroup)
                {
                    IGroupOperation pageVM = canvas.DataContext as IGroupOperation;
                    pageVM.UpdateGroup((this.designerItem.DataContext as GroupViewModel).WidgetID);
                }
            }
        }
        private void LifeTextDrawTransform(string s, DrawingContext drawingContext, Brush brush, Point p)
        {
            bool debug = false;

            FormattedText formattedText = new FormattedText(
                s,
                CultureInfo.GetCultureInfo("en-us"),
                FlowDirection.LeftToRight,
                new Typeface(new FontFamily("Chakra Petch"), FontStyles.Normal, FontWeights.Bold, FontStretches.Normal),
                18,
                brush);

            formattedText.SetFontWeight(FontWeights.Bold);

            TextAlignment text_align = TextAlignment.Center;

            // Make a transformation to center the text.
            double width = formattedText.Width -
                           formattedText.OverhangLeading;
            double             height     = formattedText.Height;
            TranslateTransform translate1 = new TranslateTransform
            {
                Y = -height / 2
            };

            if ((text_align == TextAlignment.Left) ||
                (text_align == TextAlignment.Justify))
            {
                translate1.X = -width / 2;
            }
            else if (text_align == TextAlignment.Right)
            {
                translate1.X = width / 2;
            }
            else
            {
                translate1.X = 0;
            }

            RotateTransform rotate = new RotateTransform(-35);

            // Get the text's bounding rectangle.
            Rect rect = new Rect(0, 0, width, height);

            if (text_align == TextAlignment.Center)
            {
                rect.X -= width / 2;
            }
            else if (text_align == TextAlignment.Right)
            {
                rect.X -= width;
            }

            // Get the rotated bounding rectangle.
            Rect rotated_rect = rotate.TransformBounds(rect);

            Point origin = new Point(p.X + 30, p.Y + 10);

            // Make a transformation to center the
            // bounding rectangle at the destination.
            TranslateTransform translate2 = new TranslateTransform(origin.X, origin.Y);

            TextAlignment     halign = TextAlignment.Center;
            VerticalAlignment valign = VerticalAlignment.Top;

            // Adjust the translation for the desired alignment.
            if (halign == TextAlignment.Left)
            {
                translate2.X += rotated_rect.Width / 2;
            }
            else if (halign == TextAlignment.Right)
            {
                translate2.X -= rotated_rect.Width / 2;
            }

            if (valign == VerticalAlignment.Top)
            {
                translate2.Y += rotated_rect.Height / 2;
            }
            else if (valign == VerticalAlignment.Bottom)
            {
                translate2.Y -= rotated_rect.Height / 2;
            }

            // Push transformations in reverse order. (Thanks Microsoft!)
            drawingContext.PushTransform(translate2);
            drawingContext.PushTransform(rotate);
            drawingContext.PushTransform(translate1);

            // Draw.
            drawingContext.DrawText(formattedText, new Point(0, 0));

            // Draw a rectangle around the text. (For debugging.)
            if (debug == true)
            {
                drawingContext.DrawRectangle(null, new Pen(Brushes.Red, 1), rect);
            }

            // Remove the transformations.
            drawingContext.Pop();
            drawingContext.Pop();
            drawingContext.Pop();

            // Draw the rotated bounding rectangle. (For debugging.)
            if (debug == true)
            {
                Rect transformed_rect = translate2.TransformBounds(rotate.TransformBounds(translate1.TransformBounds(rect)));
                Pen  custom_pen       = new Pen(Brushes.Blue, 1)
                {
                    DashStyle = new DashStyle(new double[] { 5, 5 }, 0)
                };
                drawingContext.DrawRectangle(null, custom_pen, transformed_rect);
            }
        }
예제 #14
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="dc"></param>
        /// <param name="line"></param>
        /// <param name="dx"></param>
        /// <param name="dy"></param>
        /// <param name="db"></param>
        /// <param name="r"></param>
        public void Draw(object dc, XLine line, double dx, double dy, ImmutableArray <ShapeProperty> db, Record r)
        {
            var _dc = dc as DrawingContext;

            var style = line.Style;

            if (style == null)
            {
                return;
            }

            double zoom                = _state.Zoom;
            double thicknessLine       = style.Thickness / zoom;
            double halfLine            = thicknessLine / 2.0;
            double thicknessStartArrow = style.StartArrowStyle.Thickness / zoom;
            double halfStartArrow      = thicknessStartArrow / 2.0;
            double thicknessEndArrow   = style.EndArrowStyle.Thickness / zoom;
            double halfEndArrow        = thicknessEndArrow / 2.0;

            // line style
            Tuple <Brush, Pen> lineCache = null;
            Brush fillLine;
            Pen   strokeLine;

            if (_enableStyleCache &&
                _styleCache.TryGetValue(style, out lineCache))
            {
                fillLine   = lineCache.Item1;
                strokeLine = lineCache.Item2;
            }
            else
            {
                fillLine   = CreateBrush(style.Fill);
                strokeLine = CreatePen(style, thicknessLine);
                if (_enableStyleCache)
                {
                    _styleCache.Add(style, Tuple.Create(fillLine, strokeLine));
                }
            }

            // start arrow style
            Tuple <Brush, Pen> startArrowCache = null;
            Brush fillStartArrow;
            Pen   strokeStartArrow;

            if (_enableArrowStyleCache &&
                _arrowStyleCache.TryGetValue(style.StartArrowStyle, out startArrowCache))
            {
                fillStartArrow   = startArrowCache.Item1;
                strokeStartArrow = startArrowCache.Item2;
            }
            else
            {
                fillStartArrow   = CreateBrush(style.StartArrowStyle.Fill);
                strokeStartArrow = CreatePen(style.StartArrowStyle, thicknessStartArrow);
                if (_enableArrowStyleCache)
                {
                    _arrowStyleCache.Add(style.StartArrowStyle, Tuple.Create(fillStartArrow, strokeStartArrow));
                }
            }

            // end arrow style
            Tuple <Brush, Pen> endArrowCache = null;
            Brush fillEndArrow;
            Pen   strokeEndArrow;

            if (_enableArrowStyleCache &&
                _arrowStyleCache.TryGetValue(style.EndArrowStyle, out endArrowCache))
            {
                fillEndArrow   = endArrowCache.Item1;
                strokeEndArrow = endArrowCache.Item2;
            }
            else
            {
                fillEndArrow   = CreateBrush(style.EndArrowStyle.Fill);
                strokeEndArrow = CreatePen(style.EndArrowStyle, thicknessEndArrow);
                if (_enableArrowStyleCache)
                {
                    _arrowStyleCache.Add(style.EndArrowStyle, Tuple.Create(fillEndArrow, strokeEndArrow));
                }
            }

            // line max length
            double x1 = line.Start.X + dx;
            double y1 = line.Start.Y + dy;
            double x2 = line.End.X + dx;
            double y2 = line.End.Y + dy;

            XLine.SetMaxLength(line, ref x1, ref y1, ref x2, ref y2);

            // arrow transforms
            var    sas = style.StartArrowStyle;
            var    eas = style.EndArrowStyle;
            double a1  = Math.Atan2(y1 - y2, x1 - x2) * 180.0 / Math.PI;
            double a2  = Math.Atan2(y2 - y1, x2 - x1) * 180.0 / Math.PI;
            bool   doRectTransform1 = a1 % 90.0 != 0.0;
            bool   doRectTransform2 = a2 % 90.0 != 0.0;

            var t1 = new RotateTransform(a1, x1, y1);
            var t2 = new RotateTransform(a2, x2, y2);

            Point pt1;
            Point pt2;

            // draw start arrow
            double radiusX1 = sas.RadiusX;
            double radiusY1 = sas.RadiusY;
            double sizeX1   = 2.0 * radiusX1;
            double sizeY1   = 2.0 * radiusY1;

            switch (sas.ArrowType)
            {
            default:
            case ArrowType.None:
            {
                pt1 = new Point(x1, y1);
            }
            break;

            case ArrowType.Rectangle:
            {
                pt1 = t1.Transform(new Point(x1 - sizeX1, y1));
                var rect = new Rect(x1 - sizeX1, y1 - radiusY1, sizeX1, sizeY1);
                if (doRectTransform1)
                {
                    _dc.PushTransform(t1);
                    DrawRectangleInternal(_dc, halfStartArrow, fillStartArrow, strokeStartArrow, sas.IsStroked, sas.IsFilled, ref rect);
                    _dc.Pop();
                }
                else
                {
                    var bounds = t1.TransformBounds(rect);
                    DrawRectangleInternal(_dc, halfStartArrow, fillStartArrow, strokeStartArrow, sas.IsStroked, sas.IsFilled, ref bounds);
                }
            }
            break;

            case ArrowType.Ellipse:
            {
                pt1 = t1.Transform(new Point(x1 - sizeX1, y1));
                _dc.PushTransform(t1);
                var c = new Point(x1 - radiusX1, y1);
                DrawEllipseInternal(_dc, halfStartArrow, fillStartArrow, strokeStartArrow, sas.IsStroked, sas.IsFilled, ref c, radiusX1, radiusY1);
                _dc.Pop();
            }
            break;

            case ArrowType.Arrow:
            {
                pt1 = t1.Transform(new Point(x1, y1));
                var p11 = t1.Transform(new Point(x1 - sizeX1, y1 + sizeY1));
                var p21 = t1.Transform(new Point(x1, y1));
                var p12 = t1.Transform(new Point(x1 - sizeX1, y1 - sizeY1));
                var p22 = t1.Transform(new Point(x1, y1));
                DrawLineInternal(_dc, halfStartArrow, strokeStartArrow, sas.IsStroked, ref p11, ref p21);
                DrawLineInternal(_dc, halfStartArrow, strokeStartArrow, sas.IsStroked, ref p12, ref p22);
            }
            break;
            }

            // draw end arrow
            double radiusX2 = eas.RadiusX;
            double radiusY2 = eas.RadiusY;
            double sizeX2   = 2.0 * radiusX2;
            double sizeY2   = 2.0 * radiusY2;

            switch (eas.ArrowType)
            {
            default:
            case ArrowType.None:
            {
                pt2 = new Point(x2, y2);
            }
            break;

            case ArrowType.Rectangle:
            {
                pt2 = t2.Transform(new Point(x2 - sizeX2, y2));
                var rect = new Rect(x2 - sizeX2, y2 - radiusY2, sizeX2, sizeY2);
                if (doRectTransform2)
                {
                    _dc.PushTransform(t2);
                    DrawRectangleInternal(_dc, halfEndArrow, fillEndArrow, strokeEndArrow, eas.IsStroked, eas.IsFilled, ref rect);
                    _dc.Pop();
                }
                else
                {
                    var bounds = t2.TransformBounds(rect);
                    DrawRectangleInternal(_dc, halfEndArrow, fillEndArrow, strokeEndArrow, eas.IsStroked, eas.IsFilled, ref bounds);
                }
            }
            break;

            case ArrowType.Ellipse:
            {
                pt2 = t2.Transform(new Point(x2 - sizeX2, y2));
                _dc.PushTransform(t2);
                var c = new Point(x2 - radiusX2, y2);
                DrawEllipseInternal(_dc, halfEndArrow, fillEndArrow, strokeEndArrow, eas.IsStroked, eas.IsFilled, ref c, radiusX2, radiusY2);
                _dc.Pop();
            }
            break;

            case ArrowType.Arrow:
            {
                pt2 = t2.Transform(new Point(x2, y2));
                var p11 = t2.Transform(new Point(x2 - sizeX2, y2 + sizeY2));
                var p21 = t2.Transform(new Point(x2, y2));
                var p12 = t2.Transform(new Point(x2 - sizeX2, y2 - sizeY2));
                var p22 = t2.Transform(new Point(x2, y2));
                DrawLineInternal(_dc, halfEndArrow, strokeEndArrow, eas.IsStroked, ref p11, ref p21);
                DrawLineInternal(_dc, halfEndArrow, strokeEndArrow, eas.IsStroked, ref p12, ref p22);
            }
            break;
            }

            // draw line using points from arrow transforms
            DrawLineInternal(_dc, halfLine, strokeLine, line.IsStroked, ref pt1, ref pt2);
        }