Пример #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="NGraphics.Custom.Models.Pen"/> class.
 /// </summary>
 /// <param name="color">Color.</param>
 /// <param name="width">Width.</param>
 public Pen(Color color, double width = 1.0)
 {
     Color = color;
     Width = width;
     LineJoin = SvgStrokeLineJoin.Miter;
     LineCap = SvgStrokeLineCap.Square;
 }
Пример #2
0
        /// <summary>
        /// Compiles the stroke to the necessary triangles to draw it.
        /// </summary>
        /// <param name="path">The path to be compiled.</param>
        /// <param name="width">The width of the stroke.</param>
        /// <param name="lineCap">The line cap method for the stroke's ends.</param>
        /// <param name="lineJoin">The line join method for the stroke's midpoints</param>
        /// <param name="miterLimit">The miter limit value.</param>
        public static CompiledDrawing CompileStroke(SvgPathSegmentList path, double width,
                                                    SvgStrokeLineCap lineCap   = SvgStrokeLineCap.Butt,
                                                    SvgStrokeLineJoin lineJoin = SvgStrokeLineJoin.Bevel,
                                                    double miterLimit          = double.PositiveInfinity)
        {
            // Return empty if stroke width == 0
            if (width == 0)
            {
                return(CompiledDrawing.Empty);
            }

            // Divide the width by 2 to cope with the SVG documentation
            var halfWidth = width / 2;

            var curves = new List <Curve>();

            // Convert each split path to a fill
            foreach (var data in path.SplitCurves())
            {
                curves.AddRange(StrokeUtils.ConvertToFill(data, halfWidth, lineCap, lineJoin, miterLimit));
            }

            // And compile
            return(CompileCurves(curves, SvgFillRule.NonZero));
        }
Пример #3
0
 /// <summary>
 /// Initializes a new instance of the <see cref="NGraphics.Custom.Models.Pen"/> class.
 /// </summary>
 public Pen()
 {
     Color = Colors.Black;
     Width = 1;
     LineJoin = SvgStrokeLineJoin.Miter;
     LineCap = SvgStrokeLineCap.Square;
 }
Пример #4
0
        internal static XPen Stroke2XPen(SvgPaintServer stroke, SvgUnit strokeWidth, SvgStrokeLineCap strokeLineCap, SvgStrokeLineJoin strokeLineJoin)
        {
            var pen = stroke is SvgColourServer stroke1 ? new XPen(Color2XColor(stroke1), strokeWidth.Value) : new XPen(XColors.Black, strokeWidth.Value);

            switch (strokeLineCap)
            {
            case SvgStrokeLineCap.Inherit:
                pen.LineCap = XLineCap.Flat;
                break;

            case SvgStrokeLineCap.Butt:
                break;

            case SvgStrokeLineCap.Round:
                pen.LineCap = XLineCap.Round;
                break;

            case SvgStrokeLineCap.Square:
                pen.LineCap = XLineCap.Square;
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            switch (strokeLineJoin)
            {
            case SvgStrokeLineJoin.Inherit:
                break;

            case SvgStrokeLineJoin.Miter:
                pen.LineJoin = XLineJoin.Miter;
                break;

            case SvgStrokeLineJoin.Round:
                pen.LineJoin = XLineJoin.Round;
                break;

            case SvgStrokeLineJoin.Bevel:
                pen.LineJoin = XLineJoin.Bevel;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(strokeLineJoin), strokeLineJoin, null);
            }

            return(pen);
        }
Пример #5
0
        public static EndCapStyle AsEndCapStyle(this SvgStrokeLineCap join)
        {
            switch (join)
            {
            case SvgStrokeLineCap.Butt:
                return(EndCapStyle.Butt);

            case SvgStrokeLineCap.Round:
                return(EndCapStyle.Round);

            case SvgStrokeLineCap.Square:
                return(EndCapStyle.Square);

            case SvgStrokeLineCap.Inherit:
            default:
                return(EndCapStyle.Butt);
            }
        }
Пример #6
0
        public static LineCap ConvertToDynamicPdf(this SvgStrokeLineCap strokeLineCap)
        {
            switch (strokeLineCap)
            {
            case SvgStrokeLineCap.Butt:
                return(LineCap.Butt);

            case SvgStrokeLineCap.Round:
                return(LineCap.Round);

            case SvgStrokeLineCap.Square:
                return(LineCap.ProjectedSquare);

            case SvgStrokeLineCap.Inherit:
                throw new Exception("Inherited value can not be resolved");

            default:
                throw new ArgumentOutOfRangeException(nameof(strokeLineCap), strokeLineCap, null);
            }
        }
Пример #7
0
        private static IEnumerable <Curve> GenerateLineCaps(Curve curve, bool atEnd, double halfWidth, SvgStrokeLineCap lineCap)
        {
            // First, get the offset of the curve
            var tangent = atEnd ? curve.ExitTangent : curve.EntryTangent;
            var p       = curve.At(atEnd ? 1 : 0);

            // Now, get the offset, properly accounting for the direction
            var offset = halfWidth * tangent.CCWPerpendicular;

            // Finally, generate the line cap
            switch (lineCap)
            {
            case SvgStrokeLineCap.Butt: yield return(Curve.Line(p + offset, p - offset)); break;

            case SvgStrokeLineCap.Round: yield return(Curve.Circle(p, halfWidth, offset, -offset, !atEnd)); break;

            case SvgStrokeLineCap.Square:
            {
                // Generate a half-square
                var ext = offset.CCWPerpendicular;
                if (atEnd)
                {
                    ext = -ext;
                }
                yield return(Curve.Line(p + offset, p + offset + ext));

                yield return(Curve.Line(p + offset + ext, p - offset + ext));

                yield return(Curve.Line(p - offset + ext, p - offset));
            }
            break;

            default: throw new ArgumentException("Unrecognized lineCap", nameof(lineCap));
            }
        }
Пример #8
0
        public static IEnumerable <Curve> ConvertToFill(CurveData data, double halfWidth, SvgStrokeLineCap lineCap,
                                                        SvgStrokeLineJoin lineJoin, double miterLimit)
        {
            // We are going to generate a "stroke fill"
            var leftCurves  = new List <Curve>();
            var rightCurves = new List <Curve>();

            // Offset each curve
            for (int i = 0; i < data.Curves.Length; i++)
            {
                leftCurves.AddRange(OffsetCurve(data.Curves[i], halfWidth));
                rightCurves.AddRange(OffsetCurve(data.Curves[i], -halfWidth));

                // Detect where the line join should be added
                if (data.IsClosed || i < data.Curves.Length - 1)
                {
                    var ik          = (i + 1) % data.Curves.Length;
                    var prevTangent = data.Curves[i].ExitTangent;
                    var nextTangent = data.Curves[ik].EntryTangent;

                    // If the angles are roughly equal, no line join
                    if (RoughlyEquals(prevTangent, nextTangent))
                    {
                        continue;
                    }
                    else
                    {
                        // Else, add to the appropriate list
                        var lineJoint = GenerateLineJoins(data.Curves[i], data.Curves[ik], halfWidth, lineJoin, miterLimit);
                        (prevTangent.Cross(nextTangent) > 0 ? rightCurves : leftCurves).AddRange(lineJoint);
                    }
                }
            }

            // If the curve is open, add the line caps
            if (!data.IsClosed)
            {
                rightCurves.InsertRange(0, GenerateLineCaps(data.Curves[0], false, halfWidth, lineCap));
                leftCurves.AddRange(GenerateLineCaps(data.Curves[data.Curves.Length - 1], true, halfWidth, lineCap));
            }

            // Reverse the inner list and each curve
            var reverseRightCurves = rightCurves.Select(c => c.Reverse).Reverse();

            // Generate the compiled fill
            return(leftCurves.Concat(reverseRightCurves).SelectMany(c => c.Simplify(true)));
        }
Пример #9
0
        private Tuple <string, ICssValue> ParseValue(string name, string value, string priority, bool presentation)
        {
            var important = priority == "important";

            if (!presentation)
            {
                name = name.ToLower();
            }

            ICssValue parsedValue = null;

            switch (name)
            {
            case "fill":
            case "stroke":
                parsedValue = new SvgPaint(value);
                break;

            case "stroke-width":
                parsedValue = SvgLength.Parse(value, presentation);
                if (((SvgLength)parsedValue).Value < 0.0F)
                {
                    return(null);
                }
                break;

            case "stroke-linecap":
                parsedValue = new SvgStrokeLineCap(value);
                break;

            case "stroke-linejoin":
                parsedValue = new SvgStrokeLineJoin(value);
                break;

            case "stroke-miterlimit":
                parsedValue = SvgNumber.Parse(value);
                break;

            case "color":
                parsedValue = new SvgColor(value);
                break;

            case "stop-color":
                parsedValue = new SvgStopColor(value);
                break;

            case "fill-opacity":
            case "stroke-opacity":
            case "stop-opacity":
            case "opacity":
                parsedValue = SvgNumber.Parse(value, 0.0F, 1.0F);
                break;

            case "clip-path":
                parsedValue = new SvgIri(value);
                break;

            case "fill-rule":
            case "clip-rule":
                parsedValue = new SvgFillRule(presentation ? value : value.ToLower());
                break;
            }

            if (!this._cache.ContainsKey(name))
            {
                var result = Tuple.Create(value, parsedValue);
                this._cache.Add(name, result);
                return(result);
            }
            else if (important)
            {
                var result = Tuple.Create(value, parsedValue);
                this._cache[name] = result;
                return(result);
            }

            return(null);
        }