WPF 图形绘制 及各种线帽、箭头的实现

 ///


    /// 矩形类
    ///

    public sealed class CRectangle : VtShape
    {
        #region Overrides
        protected override Geometry DefiningGeometry
        {
            get
            {
                // Create a StreamGeometry for describing the shape
                StreamGeometry geometry = new StreamGeometry();
                geometry.FillRule = FillRule.EvenOdd;

                using (StreamGeometryContext context = geometry.Open())
                {
                    InternalDrawArrowGeometry(context);
                }

                // Freeze the geometry for performance benefits
                geometry.Freeze();

                return geometry;
            }
        }

        #endregion
        #region Privates
        private void InternalDrawArrowGeometry(StreamGeometryContext context)
        {
            //因为线宽的不同会导致区域显示不全,因此在计算时需考虑线宽
            Point ptStart = new Point(this.StrokeThickness / 2, this.StrokeThickness / 2);
            Point pt2 = new Point(this.Width - this.StrokeThickness / 2, this.StrokeThickness / 2);
            Point pt3 = new Point(this.Width - this.StrokeThickness / 2, this.Height - this.StrokeThickness / 2);
            Point pt4 = new Point(this.StrokeThickness / 2, this.Height - this.StrokeThickness / 2);
            Point ptEnd = new Point(this.StrokeThickness / 2, 0);
            context.BeginFigure(ptStart, true, false);
            context.LineTo(pt2, true, true);
            context.LineTo(pt3, true, true);
            context.LineTo(pt4, true, true);
            context.LineTo(ptEnd, true, true);
        }

        #endregion
    }
    ///


    /// 椭圆类
    ///

    public sealed class CEllipse : VtShape
    {
        #region Overrides
        protected override Geometry DefiningGeometry
        {
            get
            {
                // Create a StreamGeometry for describing the shape
                StreamGeometry geometry = new StreamGeometry();
                geometry.FillRule = FillRule.EvenOdd;

                using (StreamGeometryContext context = geometry.Open())
                {
                    InternalDrawArrowGeometry(context);
                }

                // Freeze the geometry for performance benefits
                geometry.Freeze();

                return geometry;
            }
        }

        #endregion
        #region Privates
        private void InternalDrawArrowGeometry(StreamGeometryContext context)
        {
            //因为线宽的不同会导致区域显示不全,因此在计算时需考虑线宽
            double w = this.Width - this.StrokeThickness * 2;
            double h = this.Height - this.StrokeThickness * 2;
            if (w > 0 && h > 0)
            {
                Point pt = new Point(w / 2 + this.StrokeThickness, this.StrokeThickness);
                context.BeginFigure(pt, true, true);
                context.ArcTo(new Point(w / 2 + this.StrokeThickness, h + this.StrokeThickness), new Size(w / 2, h / 2), 0, true, SweepDirection.Counterclockwise, true, true);
                context.ArcTo(pt, new Size(w / 2, h / 2), 0, true, SweepDirection.Counterclockwise, true, true);
            }
        }

        #endregion
    }
    ///


    /// 直线类
    ///

    public sealed class CLine : VtShape
    {
        #region Dependency Properties

        public static readonly DependencyProperty X1Property = DependencyProperty.Register("X1", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
        public static readonly DependencyProperty Y1Property = DependencyProperty.Register("Y1", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
        public static readonly DependencyProperty X2Property = DependencyProperty.Register("X2", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
        public static readonly DependencyProperty Y2Property = DependencyProperty.Register("Y2", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
        #endregion

        #region CLR Properties
        [TypeConverter(typeof(LengthConverter))]
        public double X1
        {
            get { return (double)base.GetValue(X1Property); }
            set { base.SetValue(X1Property, value); }
        }

        [TypeConverter(typeof(LengthConverter))]
        public double Y1
        {
            get { return (double)base.GetValue(Y1Property); }
            set { base.SetValue(Y1Property, value); }
        }

        [TypeConverter(typeof(LengthConverter))]
        public double X2
        {
            get { return (double)base.GetValue(X2Property); }
            set { base.SetValue(X2Property, value); }
        }

        [TypeConverter(typeof(LengthConverter))]
        public double Y2
        {
            get { return (double)base.GetValue(Y2Property); }
            set { base.SetValue(Y2Property, value); }
        }
        #endregion
        #region Overrides
        protected override Geometry DefiningGeometry
        {
            get
            {
                // Create a StreamGeometry for describing the shape
                StreamGeometry geometry = new StreamGeometry();
                geometry.FillRule = FillRule.EvenOdd;

                using (StreamGeometryContext context = geometry.Open())
                {
                    InternalDrawArrowGeometry(context);
                }

                // Freeze the geometry for performance benefits
                geometry.Freeze();

                return geometry;
            }
        }
        #endregion
        #region Privates
        public CLine(Canvas linePanel)
        {
            _panel = linePanel;
            _panel.Children.Add(_arrow.lineCap_start);
            _panel.Children.Add(_arrow.lineCap_end);
        }
        private void InternalDrawArrowGeometry(StreamGeometryContext context)
        {
           
            double theta = Math.Atan2(Y1 - Y2, X1 - X2);
            double sint = Math.Sin(theta);
            double cost = Math.Cos(theta);

            Point pt1 = new Point(X1, this.Y1);
            Point pt2 = new Point(X2, this.Y2);

            Point pt3 = new Point(
                X2 + (_arrow.HeadWidth * cost - _arrow.HeadHeight * sint),
                Y2 + (_arrow.HeadWidth * sint + _arrow.HeadHeight * cost));

            Point pt4 = new Point(
                X2 + (_arrow.HeadWidth * cost + _arrow.HeadHeight * sint),
                Y2 - (_arrow.HeadHeight * cost - _arrow.HeadWidth * sint));

            Point pt5 = new Point(
              X1 - (_arrow.TailWidth * cost - _arrow.TailHeight * sint),
              Y1 - (_arrow.TailWidth * sint + _arrow.TailHeight * cost));

            Point pt6 = new Point(
                X1 - (_arrow.TailWidth * cost + _arrow.TailHeight * sint),
                Y1 + (_arrow.TailHeight * cost - _arrow.TailWidth * sint));


            //this.StrokeStartLineCap = PenLineCap.Triangle;
            //this.StrokeEndLineCap = PenLineCap.Triangle;

            //lineCap_start = new Polygon();
            PointCollection pts_start = new PointCollection();
            pts_start.Add(pt5);
            pts_start.Add(pt1);
            pts_start.Add(pt6);
            PointCollection pts_end = new PointCollection();
            pts_end.Add(pt3);
            pts_end.Add(pt2);
            pts_end.Add(pt4);
            SetLineCap( pt1, pt2, pt1, pt2);//设置线帽
     
            context.BeginFigure(pt1, false, false);
            //context.LineTo(pt5, true, true);
            //context.LineTo(pt1, true, true);
            //context.LineTo(pt6, true, true);
            //context.LineTo(pt1, true, true);
            //绘制第一个点的线帽
            context.LineTo(pt2, true, true);//绘制直线
            //context.LineTo(pt3, true, true);
            //context.LineTo(pt2, true, true);
            //context.LineTo(pt4, true, true);
            //绘制第二个点的线帽
        }
        #endregion
    }
    ///


    /// 右直角三角形类
    ///

    public sealed class CRightTriangle : VtShape
    {
        #region Overrides
        protected override Geometry DefiningGeometry
        {
            get
            {
                // Create a StreamGeometry for describing the shape
                StreamGeometry geometry = new StreamGeometry();
                geometry.FillRule = FillRule.EvenOdd;

                using (StreamGeometryContext context = geometry.Open())
                {
                    InternalDrawArrowGeometry(context);
                }

                // Freeze the geometry for performance benefits
                geometry.Freeze();

                return geometry;
            }
        }

        #endregion
        #region Privates

        private void InternalDrawArrowGeometry(StreamGeometryContext context)
        {
            double angle = Math.Atan(this.Height / (this.Width / 2));
            double offsetTop = this.StrokeThickness / Math.Cos(angle);
            double offsetLeft = this.StrokeThickness / Math.Tan(angle) + this.StrokeThickness / Math.Sign(angle);

            Point pt1 = new Point(this.Width - offsetLeft, 0);
            Point pt2 = new Point(0, this.Height - this.StrokeThickness);
            Point pt3 = new Point(this.Width - offsetLeft, this.Height - this.StrokeThickness);
            context.BeginFigure(pt1, true, true);
            context.LineTo(pt2, true, true);
            context.LineTo(pt3, true, true);
            context.LineTo(pt1, true, true);
        }

        #endregion
    }
    ///


    /// 左直角三角形类
    ///

    public sealed class CLeftTriangle : VtShape
    {
        #region Overrides
        protected override Geometry DefiningGeometry
        {
            get
            {
                // Create a StreamGeometry for describing the shape
                StreamGeometry geometry = new StreamGeometry();
                geometry.FillRule = FillRule.EvenOdd;

                using (StreamGeometryContext context = geometry.Open())
                {
                    InternalDrawArrowGeometry(context);
                }

                // Freeze the geometry for performance benefits
                geometry.Freeze();

                return geometry;
            }
        }

        #endregion
        #region Privates

        private void InternalDrawArrowGeometry(StreamGeometryContext context)
        {
            double angle = Math.Atan(this.Height / (this.Width / 2));
            double offsetTop = this.StrokeThickness / Math.Cos(angle);
            double offsetLeft = this.StrokeThickness / Math.Tan(angle) + this.StrokeThickness / Math.Sign(angle);

            Point pt1 = new Point(0, 0);
            Point pt2 = new Point(0, this.Height - this.StrokeThickness);
            Point pt3 = new Point(this.Width - offsetLeft, this.Height - this.StrokeThickness);
            context.BeginFigure(pt1, true, true);
            context.LineTo(pt2, true, true);
            context.LineTo(pt3, true, true);
            context.LineTo(pt1, true, true);
        }

        #endregion
    }
    ///


    /// 等腰三角形类
    ///

    public sealed class CTriangle : VtShape
    {
        #region Overrides
        protected override Geometry DefiningGeometry
        {
            get
            {
                // Create a StreamGeometry for describing the shape
                StreamGeometry geometry = new StreamGeometry();
                geometry.FillRule = FillRule.EvenOdd;

                using (StreamGeometryContext context = geometry.Open())
                {
                    InternalDrawArrowGeometry(context);
                }

                // Freeze the geometry for performance benefits
                geometry.Freeze();

                return geometry;
            }
        }

        #endregion
        #region Privates

        private void InternalDrawArrowGeometry(StreamGeometryContext context)
        {
            double angle = Math.Atan(this.Height / (this.Width / 2));
            double offsetTop = this.StrokeThickness / Math.Cos(angle);
            double offsetLeft = this.StrokeThickness / Math.Tan(angle) + this.StrokeThickness / Math.Sign(angle);

            Point pt1 = new Point(this.Width / 2, this.StrokeThickness);
            Point pt2 = new Point(offsetLeft, this.Height - this.StrokeThickness);
            Point pt3 = new Point(this.Width - offsetLeft, this.Height - this.StrokeThickness);
            context.BeginFigure(pt1, true, true);
            context.LineTo(pt2, true, true);
            context.LineTo(pt3, true, true);
            context.LineTo(pt1, true, true);
        }

        #endregion
    }
    ///


    /// 菱形类
    ///

    public sealed class CDiamond : VtShape
    {
        #region Overrides
        protected override Geometry DefiningGeometry
        {
            get
            {
                // Create a StreamGeometry for describing the shape
                StreamGeometry geometry = new StreamGeometry();
                geometry.FillRule = FillRule.EvenOdd;

                using (StreamGeometryContext context = geometry.Open())
                {
                    InternalDrawArrowGeometry(context);
                }

                // Freeze the geometry for performance benefits
                geometry.Freeze();

                return geometry;
            }
        }
        #endregion
        #region Privates
        private void InternalDrawArrowGeometry(StreamGeometryContext context)
        {
            double angle = Math.Atan(this.Height / (this.Width / 2));
            Point pt1 = new Point(this.Width / 2, this.StrokeThickness);
            Point pt2 = new Point(this.StrokeThickness, this.Height /2);
            Point pt3 = new Point(this.Width / 2, this.Height - this.StrokeThickness);
            Point pt4 = new Point(this.Width - this.StrokeThickness, this.Height / 2);
            context.BeginFigure(pt1, true, true);
            context.LineTo(pt1, true, true);
            context.LineTo(pt2, true, true);
            context.LineTo(pt3, true, true);
            context.LineTo(pt4, true, true);
        }

        #endregion
    }
    ///


    /// 菱形类
    ///

    public sealed class CFiveStar : VtShape
    {
        #region Overrides
        protected override Geometry DefiningGeometry
        {
            get
            {
                // Create a StreamGeometry for describing the shape
                StreamGeometry geometry = new StreamGeometry();
                geometry.FillRule = FillRule.EvenOdd;

                using (StreamGeometryContext context = geometry.Open())
                {
                    InternalDrawArrowGeometry(context);
                }

                // Freeze the geometry for performance benefits
                geometry.Freeze();

                return geometry;
            }
        }
        #endregion
        #region Privates
        private Point Intersection(Point pt1, Point pt2, Point pt3, Point pt4)
        {

            double A1 = pt1.Y - pt2.Y;
            double B1 = pt2.X - pt1.X;
            double C1 = pt1.Y * (pt2.X - pt1.X) - pt1.X * (pt2.Y - pt1.Y);
            double A2 = pt3.Y - pt4.Y, B2 = pt4.X - pt3.X, C2 = pt3.Y * (pt4.X - pt3.X) - pt3.X * (pt4.Y - pt3.Y);
            //A1 * x + B1 * y = C1
            //A2 * x + B2 * y = C2
            double D = A1 * B2 - B1 * A2, Dx = C1 * B2 - B1 * C2, Dy = A1 * C2 - A2 * C1;
            double x = Dx / (double)D, y = Dy / (double)D;
            return new Point((int)x, (int)y);
        }
        private Point RotateTheta(Point pt,Point center,double theta)
        {
            double x = center.X + (pt.X - center.X) * Math.Cos((theta * Math.PI / 180)) - (pt.Y - center.Y) * Math.Sin((theta * Math.PI / 180));
            double y = center.Y + (pt.X - center.X) * Math.Sin((theta * Math.PI / 180)) + (pt.Y - center.Y) * Math.Cos((theta * Math.PI / 180));
            return new Point(x, y);
        }
        private void InternalDrawArrowGeometry(StreamGeometryContext context)
        {
            Point ptCenter = new Point(this.Width / 2, this.Height / 2);
            double radius = this.Width / 2;
            Point[] pts = new Point[10];
            pts[0] = new Point(ptCenter.X, (int)(ptCenter.Y - radius ));
            for (int i = 1; i < 5; i++)
            {
                pts[2 * i] = RotateTheta(pts[2 * (i - 1)], ptCenter, 72.0);
                pts[2 * i].X = (int)(ptCenter.X + (pts[2 * i].X - ptCenter.X) );
                pts[2 * i].Y = (int)(ptCenter.Y + (pts[2 * i].Y - ptCenter.Y) );
            }
            //通过直线求交点的方式来取得1,3,5,7,9点坐标
            pts[1] = Intersection(pts[0], pts[4], pts[2], pts[8]);
            pts[3] = Intersection(pts[0], pts[4], pts[2], pts[6]);
            pts[5] = Intersection(pts[2], pts[6], pts[4], pts[8]);
            pts[7] = Intersection(pts[0], pts[6], pts[4], pts[8]);
            pts[9] = Intersection(pts[0], pts[6], pts[2], pts[8]);

            context.BeginFigure(pts[0], true, true);
            context.LineTo(pts[1], true, true);
            context.LineTo(pts[2], true, true);
            context.LineTo(pts[3], true, true);
            context.LineTo(pts[4], true, true);
            context.LineTo(pts[5], true, true);
            context.LineTo(pts[6], true, true);
            context.LineTo(pts[7], true, true);
            context.LineTo(pts[8], true, true);
            context.LineTo(pts[9], true, true);
        }

        #endregion
    }
    ///


    /// 三次贝塞尔曲线类
    ///

    public sealed class CBezierCurve : VtShape
    {
        #region Dependency Properties
        //public static readonly DependencyProperty X1Property = DependencyProperty.Register("X1", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
        //public static readonly DependencyProperty Y1Property = DependencyProperty.Register("Y1", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
        //public static readonly DependencyProperty X2Property = DependencyProperty.Register("X2", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
        //public static readonly DependencyProperty Y2Property = DependencyProperty.Register("Y2", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        public static readonly DependencyProperty BezierPointsProperty = DependencyProperty.Register("BezierPoints", typeof(List), typeof(CBezierCurve), new FrameworkPropertyMetadata(new List(), FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
        //public static readonly DependencyProperty BezierTmpPointProperty = DependencyProperty.Register("BezierTmpPoint", typeof(Point), typeof(CBezierCurve), new FrameworkPropertyMetadata(new Point(0,0), FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
        #endregion

        #region CLR Properties
        [TypeConverter(typeof(double))]
        public List BezierPoints
        {
            get { return (List)base.GetValue(BezierPointsProperty); }
            set { BezierPoints.Clear(); base.SetValue(BezierPointsProperty, value); }
        }
        //[TypeConverter(typeof(double))]
        //public Point BezierTmpPoint
        //{
        //    get { return (Point)base.GetValue(BezierTmpPointProperty); }
        //    set { base.SetValue(BezierTmpPointProperty, value); }
        //}
        #endregion
        #region Overrides

        protected override Geometry DefiningGeometry
        {
            get
            {
                // Create a StreamGeometry for describing the shape
                StreamGeometry geometry = new StreamGeometry();
                geometry.FillRule = FillRule.EvenOdd;

                using (StreamGeometryContext context = geometry.Open())
                {
                    InternalDrawArrowGeometry(context);
                }

                // Freeze the geometry for performance benefits
                geometry.Freeze();

                return geometry;
            }
        }
        #endregion
        #region Privates

        private List _lstCtrPoints = new List();
        public bool _bDrawing = false;//是否正在绘制
        public Point BezierTmpPoint = new Point(0, 0);
        private void InternalDrawArrowGeometry(StreamGeometryContext context)
        {
            _lstCtrPoints.Clear();
            List lstPoints = new List();
            List lstCtrPoints = new List();
            foreach (Point pt in BezierPoints)
            {
                lstPoints.Add(new Point(pt.X, pt.Y));
            }
            if (_bDrawing == true && (BezierTmpPoint.X > 0 || BezierTmpPoint.Y > 0))
            {
                lstPoints.Add(new Point(BezierTmpPoint.X, BezierTmpPoint.Y));

                for (int i = 0; i < lstPoints.Count; i++)
                {
                    lstCtrPoints.AddRange(Control1(lstPoints, i));
                    if (i < lstPoints.Count - 1)
                    {
                        _lstCtrPoints.AddRange(Control1(lstPoints, i));
                    }
                }
            }
            else if (_bDrawing == false)
            {
                for (int i = 0; i < lstPoints.Count; i++)
                {
                    lstCtrPoints.AddRange(Control1(lstPoints, i));
                    _lstCtrPoints.AddRange(Control1(lstPoints, i));
                }
            }
            if (lstPoints != null && lstPoints.Count > 1)
            {
                Point ptStart = lstPoints[0];
                Point ptEnd = lstPoints[lstPoints.Count - 1];
                //先绘制开始线帽
                SetLineCap(lstPoints[lstPoints.Count - 2], ptEnd, ptStart, lstPoints[1]);
                context.BeginFigure(ptStart, true, false);
                //再绘制曲线
                for (int i = 1; i < lstPoints.Count; i++)
                {
                    context.BezierTo(lstCtrPoints[i * 2 - 1], lstCtrPoints[i * 2], lstPoints[i], true, true);
                }
            }

        }
        private List Control1(List list, int n)
        {
            List point = new List();
            point.Add(new Point());
            point.Add(new Point());
            if (n == 0)
            {
                point[0] = list[0];
            }
            else
            {
                point[0] = Average(list[n - 1], list[n]);
            }
            if (n == list.Count - 1)
            {
                point[1] = list[list.Count - 1];
            }
            else
            {
                point[1] = Average(list[n], list[n + 1]);
            }
            Point ave = Average(point[0], point[1]);
            Point sh = Sub(list[n], ave);
            point[0] = Mul(Add(point[0], sh), list[n], 0.6);
            point[1] = Mul(Add(point[1], sh), list[n], 0.6);

            return point;
        }
        private Point Average(Point x, Point y)
        {
            return new Point((x.X + y.X) / 2, (x.Y + y.Y) / 2);
        }
        private Point Add(Point x, Point y)
        {
            return new Point(x.X + y.X, x.Y + y.Y);
        }
        private Point Sub(Point x, Point y)
        {
            return new Point(x.X - y.X, x.Y - y.Y);
        }
        private Point Mul(Point x, Point y, double d)
        {
            Point temp = Sub(x, y);
            temp = new Point(temp.X * d, temp.Y * d);
            temp = Add(y, temp);
            return temp;
        }
        #endregion
        #region Public
        public CBezierCurve(Canvas linePanel)
        {
            _panel = linePanel;
            _panel.Children.Add(_arrow.lineCap_start);
            _panel.Children.Add(_arrow.lineCap_end);
        }
        public void GetVectorRect(ref Point TopLeft, ref Point BottomRight)
        {
            if (BezierPoints.Count > 0)
            {
                TopLeft.X = BezierPoints[0].X;
                TopLeft.Y = BezierPoints[0].Y;
                BottomRight.X = BezierPoints[0].X;
                BottomRight.Y = BezierPoints[0].Y;
                #region 曲线顶点
                foreach (Point pt in BezierPoints)
                {
                    if (pt.X < TopLeft.X)
                    {
                        TopLeft.X = pt.X;
                    }
                    if (pt.Y < TopLeft.Y)
                    {
                        TopLeft.Y = pt.Y;
                    }
                    if (pt.X > BottomRight.X)
                    {
                        BottomRight.X = pt.X;
                    }
                    if (pt.Y > BottomRight.Y)
                    {
                        BottomRight.Y = pt.Y;
                    }
                }
                #endregion
                #region 曲线控制点
                foreach (Point pt in _lstCtrPoints)
                {
                    if (pt.X < TopLeft.X)
                    {
                        TopLeft.X = pt.X;
                    }
                    if (pt.Y < TopLeft.Y)
                    {
                        TopLeft.Y = pt.Y;
                    }
                    if (pt.X > BottomRight.X)
                    {
                        BottomRight.X = pt.X;
                    }
                    if (pt.Y > BottomRight.Y)
                    {
                        BottomRight.Y = pt.Y;
                    }
                }
                #endregion
            }
        }
        public void Clear()
        {
            if (BezierPoints.Count > 0)
            {
                BezierPoints.Clear();
            }
            if (_lstCtrPoints.Count > 0)
            {
                _lstCtrPoints.Clear();
            }
        }
        #endregion
    }

    ///


    /// 线帽样式
    ///

    public enum EnumArrowStyle
    {
        NULL = 0,
        STYLE1 = 1,
        STYLE2 = 2,
        STYLE3 = 3,
        STYLE4 = 4,
        STYLE5 = 5,
        STYLE6 = 6,
        STYLE7 = 7,
        STYLE8 = 8,
        STYLE9 = 9,
        STYLE10 = 10,
        STYLE11 = 11,
        STYLE12 = 12
    }
    ///
    /// 线型样式
    ///

    public enum EnumDottedStyle
    {
        NULL = 0,
        STYLE1 = 1,
        STYLE2 = 2,
        STYLE3 = 3,
        STYLE4 = 4,
        STYLE5 = 5
    }
    ///
    /// 形状类,为形状设置线型、线帽
    ///

    public class VtShape : Shape
    {
        #region Arrow Style
        public struct Arrow
        {
            public double HeadWidth;
            public double HeadHeight;
            public double TailWidth;
            public double TailHeight;
            public Polygon lineCap_start;
            public Polygon lineCap_end;
            public Arrow(double _headWidth, double _headHeight, double _tailWidth, double _tailHeight, Polygon lineCapstart, Polygon lineCapend)
            {
                HeadWidth = _headWidth;
                HeadHeight = _headHeight;
                TailWidth = _tailWidth;
                TailHeight = _tailHeight;
                lineCap_start = lineCapstart;
                lineCap_end = lineCapend;
            }
        }
        protected Arrow _arrow = new Arrow(0, 0, 0, 0, new Polygon(), new Polygon());
        public static readonly DependencyProperty ArrowStyleProperty = DependencyProperty.Register("ArrowStyle", typeof(EnumArrowStyle), typeof(VtShape), new FrameworkPropertyMetadata(EnumArrowStyle.NULL, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        protected Canvas _panel = null;
        [TypeConverter(typeof(EnumArrowStyle))]
        public EnumArrowStyle ArrowStyle
        {
            get { return (EnumArrowStyle)base.GetValue(ArrowStyleProperty); }
            set
            {
                base.SetValue(ArrowStyleProperty, value);
                #region 设置箭头大小
                switch (ArrowStyle)
                {
                    case EnumArrowStyle.NULL:
                        _arrow.HeadWidth = 0; _arrow.HeadHeight = 0; _arrow.TailWidth = 0; _arrow.TailHeight = 0;
                        break;
                    case EnumArrowStyle.STYLE1:
                        _arrow.HeadWidth = 30; _arrow.HeadHeight = 20; _arrow.TailWidth = 0; _arrow.TailHeight = 0;
                        break;
                    case EnumArrowStyle.STYLE2:
                        _arrow.HeadWidth = 30; _arrow.HeadHeight = 20; _arrow.TailWidth = 0; _arrow.TailHeight = 0;
                        break;
                    case EnumArrowStyle.STYLE3:
                        _arrow.HeadWidth = 30; _arrow.HeadHeight = 20; _arrow.TailWidth = 0; _arrow.TailHeight = 0;
                        break;
                    case EnumArrowStyle.STYLE4:
                        _arrow.HeadWidth = 30; _arrow.HeadHeight = 20; _arrow.TailWidth = 0; _arrow.TailHeight = 0;
                        break;
                    case EnumArrowStyle.STYLE5:
                        _arrow.HeadWidth = 0; _arrow.HeadHeight = 0; _arrow.TailWidth = 30; _arrow.TailHeight = 20;
                        break;
                    case EnumArrowStyle.STYLE6:
                        _arrow.HeadWidth = 0; _arrow.HeadHeight = 0; _arrow.TailWidth = 30; _arrow.TailHeight = 20;
                        break;
                    case EnumArrowStyle.STYLE7:
                        _arrow.HeadWidth = 0; _arrow.HeadHeight = 0; _arrow.TailWidth = 30; _arrow.TailHeight = 20;
                        break;
                    case EnumArrowStyle.STYLE8:
                        _arrow.HeadWidth = 0; _arrow.HeadHeight = 0; _arrow.TailWidth = 30; _arrow.TailHeight = 20;
                        break;
                    case EnumArrowStyle.STYLE9:
                        _arrow.HeadWidth = 30; _arrow.HeadHeight = 20; _arrow.TailWidth = 30; _arrow.TailHeight = 20;
                        break;
                    case EnumArrowStyle.STYLE10:
                        _arrow.HeadWidth = 30; _arrow.HeadHeight = 20; _arrow.TailWidth = 30; _arrow.TailHeight = 20;
                        break;
                    case EnumArrowStyle.STYLE11:
                        _arrow.HeadWidth = 30; _arrow.HeadHeight = 20; _arrow.TailWidth = 30; _arrow.TailHeight = 20;
                        break;
                    case EnumArrowStyle.STYLE12:
                        _arrow.HeadWidth = 30; _arrow.HeadHeight = 20; _arrow.TailWidth = 30; _arrow.TailHeight = 20;
                        break;
                }
                #endregion
            }
        }
        protected enum LineCapType
        {
            LineCap_Start =0,
            LineCap_End =1
        }
        protected void SetLineCap( Point pt1_start, Point pt1_end,Point pt2_start, Point pt2_end)
        {
            _arrow.lineCap_start.Points = new PointCollection ();
            _arrow.lineCap_end.Points = new PointCollection ();
            _arrow.lineCap_start.Fill = ((Polygon)_arrow.lineCap_start).Stroke = this.Stroke; // Brushes.Yellow;
            _arrow.lineCap_end.Fill = ((Polygon)_arrow.lineCap_end).Stroke  = this.Stroke;//= Brushes.Yellow;
            _arrow.lineCap_start.StrokeThickness = _arrow.lineCap_end.StrokeThickness = this.StrokeThickness;
            switch (ArrowStyle)
            {
                case EnumArrowStyle.NULL:
                    break;
                case EnumArrowStyle.STYLE1:
                    SetTriangle2Cap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
                case EnumArrowStyle.STYLE2:
                    SetTriangleCap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
                case EnumArrowStyle.STYLE3:
                    SetRectCap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
                case EnumArrowStyle.STYLE4:
                    SetTriangle3Cap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
                case EnumArrowStyle.STYLE5:
                    SetTriangle2Cap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
                case EnumArrowStyle.STYLE6:
                    SetTriangleCap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
                case EnumArrowStyle.STYLE7:
                    SetRectCap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
                case EnumArrowStyle.STYLE8:
                    SetTriangle3Cap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
                case EnumArrowStyle.STYLE9:
                    SetTriangle2Cap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    SetTriangle2Cap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
                case EnumArrowStyle.STYLE10:
                    SetTriangleCap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    SetTriangleCap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
                case EnumArrowStyle.STYLE11:
                    SetRectCap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    SetRectCap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
                case EnumArrowStyle.STYLE12:
                    SetTriangle3Cap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    SetTriangle3Cap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);
                    break;
            }
        }

        ///


        /// 封闭的三角形线帽
        ///

        ///
        ///
        ///
        ///
        private void SetTriangleCap(LineCapType lineCapType, Point pt_start, Point pt_end, Polygon cap_start, Polygon cap_end)
        {
           
            double theta = Math.Atan2(pt_start.Y - pt_end.Y, pt_start.X - pt_end.X);
            double sint = Math.Sin(theta);
            double cost = Math.Cos(theta);
            if (lineCapType == LineCapType.LineCap_Start )
            {
               cap_start.Points.Add(
                   new Point(
                  pt_start.X - (_arrow.TailWidth * cost - _arrow.TailHeight * sint),
                  pt_start.Y - (_arrow.TailWidth * sint + _arrow.TailHeight * cost))
                  );
               cap_start.Points.Add(new Point(pt_start.X + 20 * cost, pt_start.Y + 20 * sint));
               cap_start.Points.Add( new Point(
                    pt_start.X - (_arrow.TailWidth * cost + _arrow.TailHeight * sint),
                    pt_start.Y + (_arrow.TailHeight * cost - _arrow.TailWidth * sint))
                    );
            }
            if (lineCapType == LineCapType.LineCap_End)
            {
                cap_end.Points.Add(
                            new Point(
                                 pt_end.X + (_arrow.HeadWidth * cost - _arrow.HeadHeight * sint),
                                 pt_end.Y + (_arrow.HeadWidth * sint + _arrow.HeadHeight * cost))
                         );
                cap_end.Points.Add(new Point(pt_end.X - 20 * cost, pt_end.Y - 20 * sint));
                cap_end.Points.Add(
                 new Point(
                     pt_end.X + (_arrow.HeadWidth * cost + _arrow.HeadHeight * sint),
                      pt_end.Y - (_arrow.HeadHeight * cost - _arrow.HeadWidth * sint))
               );
            }
        }
        ///
        /// 非封闭的三角形线帽
        ///

        /// 端点1
        /// 端点2
        /// 端点1形状
        /// 端点2形状
        private void SetTriangle2Cap(LineCapType lineCapType, Point pt_start, Point pt_end, Polygon cap_start, Polygon cap_end)
        {
            double theta = Math.Atan2(pt_start.Y - pt_end.Y, pt_start.X - pt_end.X);
            double sint = Math.Sin(theta);
            double cost = Math.Cos(theta);
            if (lineCapType == LineCapType.LineCap_Start)
            {
                cap_start.Points.Add(
                    new Point(
                   pt_start.X - (_arrow.TailWidth * cost - _arrow.TailHeight * sint),
                   pt_start.Y - (_arrow.TailWidth * sint + _arrow.TailHeight * cost))
                   );
                cap_start.Points.Add(new Point(pt_start.X + 20 * cost, pt_start.Y + 20 * sint));
                cap_start.Points.Add(new Point(
                     pt_start.X - (_arrow.TailWidth * cost + _arrow.TailHeight * sint),
                     pt_start.Y + (_arrow.TailHeight * cost - _arrow.TailWidth * sint))
                     );
                cap_start.Points.Add(new Point(pt_start.X - 5 * cost, pt_start.Y - 5 * sint));
                cap_start.Points.Add(
                   new Point(
                  pt_start.X - (_arrow.TailWidth * cost - _arrow.TailHeight * sint),
                  pt_start.Y - (_arrow.TailWidth * sint + _arrow.TailHeight * cost))
                  );
            }
            if (lineCapType == LineCapType.LineCap_End)
            {
                cap_end.Points.Add(
                            new Point(
                                 pt_end.X + (_arrow.HeadWidth * cost - _arrow.HeadHeight * sint),
                                 pt_end.Y + (_arrow.HeadWidth * sint + _arrow.HeadHeight * cost))
                         );
                cap_end.Points.Add(new Point(pt_end.X , pt_end.Y ));
                cap_end.Points.Add(
                 new Point(
                     pt_end.X + (_arrow.HeadWidth * cost + _arrow.HeadHeight * sint),
                      pt_end.Y - (_arrow.HeadHeight * cost - _arrow.HeadWidth * sint))
               );
                cap_end.Points.Add(new Point(pt_end.X - 25 * cost, pt_end.Y - 25 * sint));
                cap_end.Points.Add(
                           new Point(
                                pt_end.X + (_arrow.HeadWidth * cost - _arrow.HeadHeight * sint),
                                pt_end.Y + (_arrow.HeadWidth * sint + _arrow.HeadHeight * cost))
                        );
            }
        }
        ///
        /// 非封闭的三角形线帽
        ///

        /// 端点1
        /// 端点2
        /// 端点1形状
        /// 端点2形状
        private void SetTriangle3Cap(LineCapType lineCapType, Point pt_start, Point pt_end, Polygon cap_start, Polygon cap_end)
        {
            double theta = Math.Atan2(pt_start.Y - pt_end.Y, pt_start.X - pt_end.X);
            double sint = Math.Sin(theta);
            double cost = Math.Cos(theta);
            if (lineCapType == LineCapType.LineCap_Start)
            {
                cap_start.Points.Add(
                    new Point(
                   pt_start.X - (_arrow.TailWidth * cost - _arrow.TailHeight * sint),
                   pt_start.Y - (_arrow.TailWidth * sint + _arrow.TailHeight * cost))
                   );
                cap_start.Points.Add(new Point(pt_start.X, pt_start.Y));
                cap_start.Points.Add(new Point(
                     pt_start.X - (_arrow.TailWidth * cost + _arrow.TailHeight * sint),
                     pt_start.Y + (_arrow.TailHeight * cost - _arrow.TailWidth * sint))
                     );
                cap_start.Points.Add(new Point(pt_start.X - 1 * cost, pt_start.Y - 1 * sint));
                cap_start.Points.Add(
                   new Point(
                  pt_start.X - (_arrow.TailWidth * cost - _arrow.TailHeight * sint),
                  pt_start.Y - (_arrow.TailWidth * sint + _arrow.TailHeight * cost))
                  );
            }
            if (lineCapType == LineCapType.LineCap_End)
            {
                cap_end.Points.Add(
                            new Point(
                                 pt_end.X + (_arrow.HeadWidth * cost - _arrow.HeadHeight * sint),
                                 pt_end.Y + (_arrow.HeadWidth * sint + _arrow.HeadHeight * cost))
                         );
                cap_end.Points.Add(new Point(pt_end.X, pt_end.Y));
                cap_end.Points.Add(
                 new Point(
                     pt_end.X + (_arrow.HeadWidth * cost + _arrow.HeadHeight * sint),
                      pt_end.Y - (_arrow.HeadHeight * cost - _arrow.HeadWidth * sint))
               );
                cap_end.Points.Add(new Point(pt_end.X - 1 * cost, pt_end.Y - 1 * sint));
                cap_end.Points.Add(
                           new Point(
                                pt_end.X + (_arrow.HeadWidth * cost - _arrow.HeadHeight * sint),
                                pt_end.Y + (_arrow.HeadWidth * sint + _arrow.HeadHeight * cost))
                        );
            }
        }
        ///
        /// 封闭矩形线帽
        ///

        /// 端点1
        /// 端点2
        /// 端点1形状
        /// 端点2形状
        private void SetRectCap(LineCapType lineCapType, Point pt_start, Point pt_end, Polygon cap_start, Polygon cap_end)
        {
            double theta = Math.Atan2(pt_start.Y - pt_end.Y, pt_start.X - pt_end.X);
            double sint = Math.Sin(theta);
            double cost = Math.Cos(theta);
            if (lineCapType == LineCapType.LineCap_Start)
            {
                cap_start.Points.Add(
                    new Point(
                   pt_start.X - (_arrow.TailWidth * cost - _arrow.TailHeight * sint),
                   pt_start.Y - (_arrow.TailWidth * sint + _arrow.TailHeight * cost))
                   );
                cap_start.Points.Add(new Point(pt_start.X , pt_start.Y ));
                cap_start.Points.Add(new Point(
                     pt_start.X - (_arrow.TailWidth * cost + _arrow.TailHeight * sint),
                     pt_start.Y + (_arrow.TailHeight * cost - _arrow.TailWidth * sint))
                     );
                cap_start.Points.Add(new Point(pt_start.X - 60 * cost, pt_start.Y - 60 * sint));
                cap_start.Points.Add(
                   new Point(
                  pt_start.X - (_arrow.TailWidth * cost - _arrow.TailHeight * sint),
                  pt_start.Y - (_arrow.TailWidth * sint + _arrow.TailHeight * cost))
                  );
            }
            if (lineCapType == LineCapType.LineCap_End)
            {
                cap_end.Points.Add(
                            new Point(
                                 pt_end.X + (_arrow.HeadWidth * cost - _arrow.HeadHeight * sint),
                                 pt_end.Y + (_arrow.HeadWidth * sint + _arrow.HeadHeight * cost))
                         );
                cap_end.Points.Add(new Point(pt_end.X + 60 * cost, pt_end.Y + 60 * sint));
                cap_end.Points.Add(
                 new Point(
                     pt_end.X + (_arrow.HeadWidth * cost + _arrow.HeadHeight * sint),
                      pt_end.Y - (_arrow.HeadHeight * cost - _arrow.HeadWidth * sint))
               );
                cap_end.Points.Add(new Point(pt_end.X, pt_end.Y));
                cap_end.Points.Add(
                           new Point(
                                pt_end.X + (_arrow.HeadWidth * cost - _arrow.HeadHeight * sint),
                                pt_end.Y + (_arrow.HeadWidth * sint + _arrow.HeadHeight * cost))
                        );
            }
        }
        #endregion
        #region Dotted Style
        private EnumDottedStyle _DottedStyle = EnumDottedStyle.NULL;
        public EnumDottedStyle DottedStyle
        {
            set { _DottedStyle = value; this.StrokeDashArray = SetDottedStyle(_DottedStyle); }
            get { return _DottedStyle; }
        }
        protected DoubleCollection SetDottedStyle(EnumDottedStyle style)
        {
            DoubleCollection dotted = new DoubleCollection();
            if (style == EnumDottedStyle.NULL)
            {
                dotted = null;
                return dotted;
            }
            if (dotted == null)
            {
                dotted = new DoubleCollection();
            }
            dotted.Clear();
            switch (style)
            {
                case EnumDottedStyle.STYLE1:
                    dotted.Add(3); dotted.Add(1);
                    break;
                case EnumDottedStyle.STYLE2:
                    dotted.Add(1); dotted.Add(1); dotted.Add(4); dotted.Add(1);
                    break;
                case EnumDottedStyle.STYLE3:
                    dotted.Add(7); dotted.Add(2);
                    break;
                case EnumDottedStyle.STYLE4:
                    dotted.Add(5); dotted.Add(1);
                    break;
                case EnumDottedStyle.STYLE5:
                    dotted.Add(1); dotted.Add(1);
                    break;
            }
            return dotted;
        }
        #endregion
        #region Overrides
        protected override Geometry DefiningGeometry
        {
            get
            {
                // Create a StreamGeometry for describing the shape
                StreamGeometry geometry = new StreamGeometry();
                geometry.FillRule = FillRule.EvenOdd;
                geometry.Freeze();
                return geometry;
            }
        }
        #endregion
    }

 

效果图如下:

WPF 图形绘制 及各种线帽、箭头的实现_第1张图片

WPF 图形绘制 及各种线帽、箭头的实现_第2张图片

你可能感兴趣的:(WPF 图形绘制 及各种线帽、箭头的实现)