Browse Source

IGeometryContext implementation

pull/1661/head
Benedikt Schroeder 8 years ago
parent
commit
b8f8c1ef0f
  1. 22
      src/Avalonia.Visuals/Media/PathGeometry.cs
  2. 198
      src/Avalonia.Visuals/Media/PathMarkupParser.cs
  3. 15
      src/Avalonia.Visuals/Media/StreamGeometry.cs
  4. 2
      src/Avalonia.Visuals/Media/StreamGeometryContext.cs
  5. 66
      src/Avalonia.Visuals/Platform/IGeometryContext.cs
  6. 54
      src/Avalonia.Visuals/Platform/IStreamGeometryContextImpl.cs
  7. 85
      src/Avalonia.Visuals/Platform/PathGeometryContext.cs
  8. 22
      tests/Avalonia.Benchmarks/Visuals/Media/PathMarkupParserTests.cs
  9. 61
      tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs

22
src/Avalonia.Visuals/Media/PathGeometry.cs

@ -8,6 +8,8 @@ using Avalonia.Platform;
namespace Avalonia.Media
{
using Avalonia.Visuals.Platform;
public class PathGeometry : StreamGeometry
{
/// <summary>
@ -28,7 +30,7 @@ namespace Avalonia.Media
static PathGeometry()
{
FiguresProperty.Changed.AddClassHandler<PathGeometry>((s, e) =>
FiguresProperty.Changed.AddClassHandler<PathGeometry>((s, e) =>
s.OnFiguresChanged(e.NewValue as PathFigures));
}
@ -40,6 +42,24 @@ namespace Avalonia.Media
Figures = new PathFigures();
}
/// <summary>
/// Parses the specified path data to a <see cref="PathGeometry"/>.
/// </summary>
/// <param name="pathData">The s.</param>
/// <returns></returns>
public static new PathGeometry Parse(string pathData)
{
var pathGeometry = new PathGeometry();
using (var context = new PathGeometryContext(pathGeometry))
using (var parser = new PathMarkupParser(context))
{
parser.Parse(pathData);
}
return pathGeometry;
}
/// <summary>
/// Gets or sets the figures.
/// </summary>

198
src/Avalonia.Visuals/Media/PathMarkupParser.cs

@ -8,6 +8,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Avalonia.Platform;
namespace Avalonia.Media
{
@ -17,13 +18,6 @@ namespace Avalonia.Media
public class PathMarkupParser : IDisposable
{
private static readonly string s_separatorPattern;
private Point _currentPoint;
private Point? _previousControlPoint;
private PathGeometry _currentGeometry;
private PathFigure _currentFigure;
private bool _isDisposed;
private static readonly Dictionary<char, Command> s_commands =
new Dictionary<char, Command>
{
@ -40,11 +34,32 @@ namespace Avalonia.Media
{ 'Z', Command.Close },
};
private IGeometryContext _geometryContext;
private Point _currentPoint;
private Point? _previousControlPoint;
private bool? _isOpen;
private bool _isDisposed;
static PathMarkupParser()
{
s_separatorPattern = CreatesSeparatorPattern();
}
/// <summary>
/// Initializes a new instance of the <see cref="PathMarkupParser"/> class.
/// </summary>
/// <param name="geometryContext">The geometry context.</param>
/// <exception cref="ArgumentNullException">geometryContext</exception>
public PathMarkupParser(IGeometryContext geometryContext)
{
if (geometryContext == null)
{
throw new ArgumentNullException(nameof(geometryContext));
}
_geometryContext = geometryContext;
}
private enum Command
{
None,
@ -61,13 +76,15 @@ namespace Avalonia.Media
Close
}
public PathGeometry Parse(string s)
/// <summary>
/// Parses the specified path data and writes the result to the geometryContext of this instance.
/// </summary>
/// <param name="pathData">The path data.</param>
public void Parse(string pathData)
{
_currentGeometry = new PathGeometry();
var tokens = ParseTokens(s);
var tokens = ParseTokens(pathData);
return CreateGeometry(tokens);
CreateGeometry(tokens);
}
void IDisposable.Dispose()
@ -84,9 +101,7 @@ namespace Avalonia.Media
if (disposing)
{
_currentFigure = null;
_currentGeometry = null;
_geometryContext = null;
}
_isDisposed = true;
@ -118,11 +133,9 @@ namespace Avalonia.Media
return center + -dir;
}
private PathGeometry CreateGeometry(IEnumerable<CommandToken> commandTokens)
private void CreateGeometry(IEnumerable<CommandToken> commandTokens)
{
_currentGeometry = new PathGeometry();
_currentPoint = new Point();
_currentPoint = new Point();
foreach (var commandToken in commandTokens)
{
@ -188,46 +201,32 @@ namespace Avalonia.Media
break;
}
}
return _currentGeometry;
}
private void SetFillRule(CommandToken commandToken)
{
_currentGeometry.FillRule = commandToken.ReadFillRule();
}
private void CloseFigure()
private void CreateFigure()
{
if (_currentFigure != null && !_currentFigure.IsClosed)
{
_currentFigure.IsClosed = true;
}
_previousControlPoint = null;
_geometryContext.BeginFigure(_currentPoint);
_currentFigure = null;
this._isOpen = true;
}
private void CreateFigure()
private void SetFillRule(CommandToken commandToken)
{
_currentFigure = new PathFigure
{
StartPoint = _currentPoint,
IsClosed = false
};
var fillRule = commandToken.ReadFillRule();
_currentGeometry.Figures.Add(_currentFigure);
_geometryContext.SetFillRule(fillRule);
}
private void AddSegment(PathSegment segment)
private void CloseFigure()
{
if (_currentFigure == null)
if (_isOpen == true)
{
CreateFigure();
_geometryContext.EndFigure(true);
}
_currentFigure.Segments.Add(segment);
_previousControlPoint = null;
_isOpen = null;
}
private void AddMove(CommandToken commandToken)
@ -266,66 +265,64 @@ namespace Avalonia.Media
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
var lineSegment = new LineSegment
if (_isOpen == null)
{
Point = _currentPoint
};
CreateFigure();
}
AddSegment(lineSegment);
_geometryContext.LineTo(_currentPoint);
}
private void AddHorizontalLine(CommandToken commandToken)
{
_currentPoint = commandToken.IsRelative
? new Point(_currentPoint.X + commandToken.ReadDouble(), _currentPoint.Y)
: _currentPoint.WithX(commandToken.ReadDouble());
? new Point(_currentPoint.X + commandToken.ReadDouble(), _currentPoint.Y)
: _currentPoint.WithX(commandToken.ReadDouble());
var lineSegment = new LineSegment
if (_isOpen == null)
{
Point = _currentPoint
};
CreateFigure();
}
AddSegment(lineSegment);
_geometryContext.LineTo(_currentPoint);
}
private void AddVerticalLine(CommandToken commandToken)
{
_currentPoint = commandToken.IsRelative
? new Point(_currentPoint.X, _currentPoint.Y + commandToken.ReadDouble())
: _currentPoint.WithY(commandToken.ReadDouble());
? new Point(_currentPoint.X, _currentPoint.Y + commandToken.ReadDouble())
: _currentPoint.WithY(commandToken.ReadDouble());
var lineSegment = new LineSegment
if (_isOpen == null)
{
Point = _currentPoint
};
CreateFigure();
}
AddSegment(lineSegment);
_geometryContext.LineTo(_currentPoint);
}
private void AddCubicBezierCurve(CommandToken commandToken)
{
var point1 = commandToken.IsRelative
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
var point2 = commandToken.IsRelative
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
_previousControlPoint = point2;
var point3 = commandToken.IsRelative
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
var bezierSegment = new BezierSegment
if (_isOpen == null)
{
Point1 = point1,
Point2 = point2,
Point3 = point3
};
CreateFigure();
}
AddSegment(bezierSegment);
_geometryContext.CubicBezierTo(point1, point2, point3);
_currentPoint = point3;
}
@ -333,22 +330,21 @@ namespace Avalonia.Media
private void AddQuadraticBezierCurve(CommandToken commandToken)
{
var start = commandToken.IsRelative
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
_previousControlPoint = start;
var end = commandToken.IsRelative
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
var quadraticBezierSegment = new QuadraticBezierSegment
if (_isOpen == null)
{
Point1 = start,
Point2 = end
};
CreateFigure();
}
AddSegment(quadraticBezierSegment);
_geometryContext.QuadraticBezierTo(start, end);
_currentPoint = end;
}
@ -356,8 +352,8 @@ namespace Avalonia.Media
private void AddSmoothCubicBezierCurve(CommandToken commandToken)
{
var point2 = commandToken.IsRelative
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
var end = commandToken.IsRelative
? commandToken.ReadRelativePoint(_currentPoint)
@ -368,10 +364,12 @@ namespace Avalonia.Media
_previousControlPoint = MirrorControlPoint((Point)_previousControlPoint, _currentPoint);
}
var bezierSegment =
new BezierSegment { Point1 = _previousControlPoint ?? _currentPoint, Point2 = point2, Point3 = end };
if (_isOpen == null)
{
CreateFigure();
}
AddSegment(bezierSegment);
_geometryContext.CubicBezierTo(_previousControlPoint ?? _currentPoint, point2, end);
_previousControlPoint = point2;
@ -389,13 +387,12 @@ namespace Avalonia.Media
_previousControlPoint = MirrorControlPoint((Point)_previousControlPoint, _currentPoint);
}
var quadraticBezierSegment = new QuadraticBezierSegment
if (_isOpen == null)
{
Point1 = _previousControlPoint ?? _currentPoint,
Point2 = end
};
CreateFigure();
}
AddSegment(quadraticBezierSegment);
_geometryContext.QuadraticBezierTo(_previousControlPoint ?? _currentPoint, end);
_currentPoint = end;
}
@ -414,16 +411,12 @@ namespace Avalonia.Media
? commandToken.ReadRelativePoint(_currentPoint)
: commandToken.ReadPoint();
var arcSegment = new ArcSegment
if (_isOpen == null)
{
Size = size,
RotationAngle = rotationAngle,
IsLargeArc = isLargeArc,
SweepDirection = sweepDirection,
Point = end
};
CreateFigure();
}
AddSegment(arcSegment);
_geometryContext.ArcTo(end, size, rotationAngle, isLargeArc, sweepDirection);
_currentPoint = end;
@ -589,10 +582,7 @@ namespace Avalonia.Media
return new Point(origin.X + x, origin.Y + y);
}
private static bool ReadCommand(
TextReader reader,
ref Command command,
ref bool relative)
private static bool ReadCommand(TextReader reader, ref Command command, ref bool relative)
{
ReadWhitespace(reader);

15
src/Avalonia.Visuals/Media/StreamGeometry.cs

@ -5,6 +5,8 @@ using Avalonia.Platform;
namespace Avalonia.Media
{
using Avalonia.Visuals.Platform;
/// <summary>
/// Represents the geometry of an arbitrarily complex shape.
/// </summary>
@ -35,10 +37,15 @@ namespace Avalonia.Media
/// <returns>A <see cref="StreamGeometry"/>.</returns>
public static new StreamGeometry Parse(string s)
{
using (var parser = new PathMarkupParser())
{
return parser.Parse(s);
}
var streamGeometry = new StreamGeometry();
using (var context = streamGeometry.Open())
using (var parser = new PathMarkupParser(context))
{
parser.Parse(s);
}
return streamGeometry;
}
/// <inheritdoc/>

2
src/Avalonia.Visuals/Media/StreamGeometryContext.cs

@ -15,7 +15,7 @@ namespace Avalonia.Media
/// <see cref="StreamGeometry.Open"/>.
/// </remarks>
/// TODO: This class is just a wrapper around IStreamGeometryContextImpl: is it needed?
public class StreamGeometryContext : IDisposable
public class StreamGeometryContext : IGeometryContext
{
private readonly IStreamGeometryContextImpl _impl;

66
src/Avalonia.Visuals/Platform/IGeometryContext.cs

@ -0,0 +1,66 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Media;
namespace Avalonia.Platform
{
/// <summary>
/// Describes a geometry using drawing commands.
/// </summary>
public interface IGeometryContext : IDisposable
{
/// <summary>
/// Draws an arc to the specified point.
/// </summary>
/// <param name="point">The destination point.</param>
/// <param name="size">The radii of an oval whose perimeter is used to draw the angle.</param>
/// <param name="rotationAngle">The rotation angle of the oval that specifies the curve.</param>
/// <param name="isLargeArc">true to draw the arc greater than 180 degrees; otherwise, false.</param>
/// <param name="sweepDirection">
/// A value that indicates whether the arc is drawn in the Clockwise or Counterclockwise direction.
/// </param>
void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection);
/// <summary>
/// Begins a new figure.
/// </summary>
/// <param name="startPoint">The starting point for the figure.</param>
/// <param name="isFilled">Whether the figure is filled.</param>
void BeginFigure(Point startPoint, bool isFilled = true);
/// <summary>
/// Draws a Bezier curve to the specified point.
/// </summary>
/// <param name="point1">The first control point used to specify the shape of the curve.</param>
/// <param name="point2">The second control point used to specify the shape of the curve.</param>
/// <param name="point3">The destination point for the end of the curve.</param>
void CubicBezierTo(Point point1, Point point2, Point point3);
/// <summary>
/// Draws a quadratic Bezier curve to the specified point
/// </summary>
/// <param name="control">Control point</param>
/// <param name="endPoint">DestinationPoint</param>
void QuadraticBezierTo(Point control, Point endPoint);
/// <summary>
/// Draws a line to the specified point.
/// </summary>
/// <param name="point">The destination point.</param>
void LineTo(Point point);
/// <summary>
/// Ends the figure started by <see cref="BeginFigure(Point, bool)"/>.
/// </summary>
/// <param name="isClosed">Whether the figure is closed.</param>
void EndFigure(bool isClosed);
/// <summary>
/// Sets the fill rule.
/// </summary>
/// <param name="fillRule">The fill rule.</param>
void SetFillRule(FillRule fillRule);
}
}

54
src/Avalonia.Visuals/Platform/IStreamGeometryContextImpl.cs

@ -1,62 +1,12 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Media;
namespace Avalonia.Platform
{
/// <summary>
/// Describes a geometry using drawing commands.
/// </summary>
public interface IStreamGeometryContextImpl : IDisposable
{
/// <summary>
/// Draws an arc to the specified point.
/// </summary>
/// <param name="point">The destination point.</param>
/// <param name="size">The radii of an oval whose perimeter is used to draw the angle.</param>
/// <param name="rotationAngle">The rotation angle of the oval that specifies the curve.</param>
/// <param name="isLargeArc">true to draw the arc greater than 180 degrees; otherwise, false.</param>
/// <param name="sweepDirection">
/// A value that indicates whether the arc is drawn in the Clockwise or Counterclockwise direction.
/// </param>
void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection);
/// <summary>
/// Begins a new figure.
/// </summary>
/// <param name="startPoint">The starting point for the figure.</param>
/// <param name="isFilled">Whether the figure is filled.</param>
void BeginFigure(Point startPoint, bool isFilled);
/// <summary>
/// Draws a Bezier curve to the specified point.
/// </summary>
/// <param name="point1">The first control point used to specify the shape of the curve.</param>
/// <param name="point2">The second control point used to specify the shape of the curve.</param>
/// <param name="point3">The destination point for the end of the curve.</param>
void CubicBezierTo(Point point1, Point point2, Point point3);
/// <summary>
/// Draws a quadratic Bezier curve to the specified point
/// </summary>
/// <param name="control">Control point</param>
/// <param name="endPoint">DestinationPoint</param>
void QuadraticBezierTo(Point control, Point endPoint);
/// <summary>
/// Draws a line to the specified point.
/// </summary>
/// <param name="point">The destination point.</param>
void LineTo(Point point);
/// <summary>
/// Ends the figure started by <see cref="BeginFigure(Point, bool)"/>.
/// </summary>
/// <param name="isClosed">Whether the figure is closed.</param>
void EndFigure(bool isClosed);
void SetFillRule(FillRule fillRule);
public interface IStreamGeometryContextImpl : IGeometryContext
{
}
}

85
src/Avalonia.Visuals/Platform/PathGeometryContext.cs

@ -0,0 +1,85 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Media;
using Avalonia.Platform;
using System;
namespace Avalonia.Visuals.Platform
{
public class PathGeometryContext : IGeometryContext
{
private PathFigure _currentFigure;
private PathGeometry _pathGeometry;
public PathGeometryContext(PathGeometry pathGeometry)
{
_pathGeometry = pathGeometry ?? throw new ArgumentNullException(nameof(pathGeometry));
}
public void Dispose()
{
_pathGeometry = null;
}
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
{
var arcSegment = new ArcSegment
{
Size = size,
RotationAngle = rotationAngle,
IsLargeArc = isLargeArc,
SweepDirection = sweepDirection,
Point = point
};
_currentFigure.Segments.Add(arcSegment);
}
public void BeginFigure(Point startPoint, bool isFilled)
{
_currentFigure = new PathFigure { StartPoint = startPoint, IsClosed = false, IsFilled = isFilled };
_pathGeometry.Figures.Add(_currentFigure);
}
public void CubicBezierTo(Point point1, Point point2, Point point3)
{
var bezierSegment = new BezierSegment { Point1 = point1, Point2 = point2, Point3 = point3 };
_currentFigure.Segments.Add(bezierSegment);
}
public void QuadraticBezierTo(Point control, Point endPoint)
{
var quadraticBezierSegment = new QuadraticBezierSegment { Point1 = control, Point2 = endPoint };
_currentFigure.Segments.Add(quadraticBezierSegment);
}
public void LineTo(Point point)
{
var lineSegment = new LineSegment
{
Point = point
};
_currentFigure.Segments.Add(lineSegment);
}
public void EndFigure(bool isClosed)
{
if (_currentFigure != null)
{
_currentFigure.IsClosed = isClosed;
}
_currentFigure = null;
}
public void SetFillRule(FillRule fillRule)
{
_pathGeometry.FillRule = fillRule;
}
}
}

22
tests/Avalonia.Benchmarks/Visuals/Media/PathMarkupParserTests.cs

@ -26,14 +26,20 @@ namespace Avalonia.Benchmarks.Visuals.Media
[Benchmark]
public void Parse_Large_Path()
{
var path = "F1 M 16.6309 18.6563C 17.1309 8.15625 29.8809 14.1563 29.8809 14.1563C 30.8809 11.1563 34.1308 11.4063" +
" 34.1308 11.4063C 33.5 12 34.6309 13.1563 34.6309 13.1563C 32.1309 13.1562 31.1309 14.9062 31.1309 14.9" +
"062C 41.1309 23.9062 32.6309 27.9063 32.6309 27.9062C 24.6309 24.9063 21.1309 22.1562 16.6309 18.6563 Z" +
" M 16.6309 19.9063C 21.6309 24.1563 25.1309 26.1562 31.6309 28.6562C 31.6309 28.6562 26.3809 39.1562 18" +
".3809 36.1563C 18.3809 36.1563 18 38 16.3809 36.9063C 15 36 16.3809 34.9063 16.3809 34.9063C 16.3809 34" +
".9063 10.1309 30.9062 16.6309 19.9063 Z ";
var parser = new PathMarkupParser();
var result = parser.Parse(path);
const string PathData = "F1 M 16.6309 18.6563C 17.1309 8.15625 29.8809 14.1563 29.8809 14.1563C 30.8809 11.1563 34.1308 11.4063" +
" 34.1308 11.4063C 33.5 12 34.6309 13.1563 34.6309 13.1563C 32.1309 13.1562 31.1309 14.9062 31.1309 14.9" +
"062C 41.1309 23.9062 32.6309 27.9063 32.6309 27.9062C 24.6309 24.9063 21.1309 22.1562 16.6309 18.6563 Z" +
" M 16.6309 19.9063C 21.6309 24.1563 25.1309 26.1562 31.6309 28.6562C 31.6309 28.6562 26.3809 39.1562 18" +
".3809 36.1563C 18.3809 36.1563 18 38 16.3809 36.9063C 15 36 16.3809 34.9063 16.3809 34.9063C 16.3809 34" +
".9063 10.1309 30.9062 16.6309 19.9063 Z ";
var streamGeometry = new StreamGeometry();
using (var context = streamGeometry.Open())
using (var parser = new PathMarkupParser(context))
{
parser.Parse(PathData);
}
}
}
}

61
tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs

@ -2,24 +2,23 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Media;
using Avalonia.Platform;
using Moq;
using Avalonia.Visuals.Platform;
using Xunit;
namespace Avalonia.Visuals.UnitTests.Media
{
using System.Linq;
public class PathMarkupParserTests
{
[Fact]
public void Parses_Move()
{
using (var parser = new PathMarkupParser())
var pathGeometry = new PathGeometry();
using (var context = new PathGeometryContext(pathGeometry))
using (var parser = new PathMarkupParser(context))
{
var geometry = parser.Parse("M10 10");
parser.Parse("M10 10");
var figure = geometry.Figures.First();
var figure = pathGeometry.Figures[0];
Assert.Equal(new Point(10, 10), figure.StartPoint);
}
@ -28,13 +27,15 @@ namespace Avalonia.Visuals.UnitTests.Media
[Fact]
public void Parses_Line()
{
using (var parser = new PathMarkupParser())
var pathGeometry = new PathGeometry();
using (var context = new PathGeometryContext(pathGeometry))
using (var parser = new PathMarkupParser(context))
{
var geometry = parser.Parse("M0 0L10 10");
parser.Parse("M0 0L10 10");
var figure = geometry.Figures.First();
var figure = pathGeometry.Figures[0];
var segment = figure.Segments.First();
var segment = figure.Segments[0];
Assert.IsType<LineSegment>(segment);
@ -47,11 +48,13 @@ namespace Avalonia.Visuals.UnitTests.Media
[Fact]
public void Parses_Close()
{
using (var parser = new PathMarkupParser())
var pathGeometry = new PathGeometry();
using (var context = new PathGeometryContext(pathGeometry))
using (var parser = new PathMarkupParser(context))
{
var geometry = parser.Parse("M0 0L10 10z");
parser.Parse("M0 0L10 10z");
var figure = geometry.Figures.First();
var figure = pathGeometry.Figures[0];
Assert.True(figure.IsClosed);
}
@ -60,11 +63,13 @@ namespace Avalonia.Visuals.UnitTests.Media
[Fact]
public void Parses_FillMode_Before_Move()
{
using (var parser = new PathMarkupParser())
var pathGeometry = new PathGeometry();
using (var context = new PathGeometryContext(pathGeometry))
using (var parser = new PathMarkupParser(context))
{
var geometry = parser.Parse("F 1M0,0");
parser.Parse("F 1M0,0");
Assert.Equal(FillRule.NonZero, geometry.FillRule);
Assert.Equal(FillRule.NonZero, pathGeometry.FillRule);
}
}
@ -74,11 +79,13 @@ namespace Avalonia.Visuals.UnitTests.Media
[InlineData("M0,0,10,10,20,20")]
public void Parses_Implicit_Line_Command_After_Move(string pathData)
{
using (var parser = new PathMarkupParser())
var pathGeometry = new PathGeometry();
using (var context = new PathGeometryContext(pathGeometry))
using (var parser = new PathMarkupParser(context))
{
var geometry = parser.Parse(pathData);
parser.Parse(pathData);
var figure = geometry.Figures[0];
var figure = pathGeometry.Figures[0];
var segment = figure.Segments[0];
@ -88,7 +95,7 @@ namespace Avalonia.Visuals.UnitTests.Media
Assert.Equal(new Point(10, 10), lineSegment.Point);
figure = geometry.Figures[1];
figure = pathGeometry.Figures[1];
segment = figure.Segments[0];
@ -106,11 +113,13 @@ namespace Avalonia.Visuals.UnitTests.Media
[InlineData("m0,0,10,10,20,20")]
public void Parses_Implicit_Line_Command_After_Relative_Move(string pathData)
{
using (var parser = new PathMarkupParser())
var pathGeometry = new PathGeometry();
using (var context = new PathGeometryContext(pathGeometry))
using (var parser = new PathMarkupParser(context))
{
var geometry = parser.Parse(pathData);
parser.Parse(pathData);
var figure = geometry.Figures[0];
var figure = pathGeometry.Figures[0];
var segment = figure.Segments[0];
@ -155,7 +164,9 @@ namespace Avalonia.Visuals.UnitTests.Media
"12.461,8.046C14.45,8.278,16,9.949,16,12")]
public void Should_Parse(string pathData)
{
using (var parser = new PathMarkupParser())
var pathGeometry = new PathGeometry();
using (var context = new PathGeometryContext(pathGeometry))
using (var parser = new PathMarkupParser(context))
{
parser.Parse(pathData);

Loading…
Cancel
Save