Browse Source

Some work on Paths.

Make paths with Stretch != None correctly fill the Path control's
available size. Added a Path_Tick_Scaled test with a path from
MahApps.Metro to test this.
pull/13/head
Steven Kirk 12 years ago
parent
commit
7fe1f7e814
  1. 5
      Cairo/Perspex.Cairo/Media/StreamGeometryContextImpl.cs
  2. 13
      Cairo/Perspex.Cairo/Media/StreamGeometryImpl.cs
  3. 45
      Perspex.Controls/Shapes/Shape.cs
  4. 10
      Perspex.SceneGraph/Matrix.cs
  5. 22
      Perspex.SceneGraph/Media/Geometry.cs
  6. 34
      Perspex.SceneGraph/Media/MatrixTransform.cs
  7. 66
      Perspex.SceneGraph/Media/PathMarkupParser.cs
  8. 6
      Perspex.SceneGraph/Media/RectangleGeometry.cs
  9. 11
      Perspex.SceneGraph/Media/StreamGeometry.cs
  10. 5
      Perspex.SceneGraph/Media/StreamGeometryContext.cs
  11. 1
      Perspex.SceneGraph/Perspex.SceneGraph.csproj
  12. 2
      Perspex.SceneGraph/Platform/IGeometryImpl.cs
  13. 2
      Perspex.SceneGraph/Platform/IStreamGeometryContextImpl.cs
  14. 2
      Perspex.SceneGraph/Platform/IStreamGeometryImpl.cs
  15. 10
      Perspex.SceneGraph/Point.cs
  16. BIN
      TestFiles/Direct2D1/Shapes/Path/Path_Tick_Scaled.expected.png
  17. 24
      Windows/Perspex.Direct2D1.RenderTests/Shapes/PathTests.cs
  18. 44
      Windows/Perspex.Direct2D1/Media/GeometryImpl.cs
  19. 10
      Windows/Perspex.Direct2D1/Media/StreamGeometryContextImpl.cs
  20. 33
      Windows/Perspex.Direct2D1/Media/StreamGeometryImpl.cs

5
Cairo/Perspex.Cairo/Media/StreamGeometryContextImpl.cs

@ -20,6 +20,11 @@ namespace Perspex.Cairo.Media
// TODO: Implement // TODO: Implement
} }
public void BezierTo(Point point1, Point point2, Point point3)
{
// TODO: Implement
}
public void LineTo(Point point) public void LineTo(Point point)
{ {
// TODO: Implement // TODO: Implement

13
Cairo/Perspex.Cairo/Media/StreamGeometryImpl.cs

@ -23,6 +23,19 @@ namespace Perspex.Cairo.Media
get { return new Rect(); } get { return new Rect(); }
} }
// TODO: Implement
public Matrix Transform
{
get { return Matrix.Identity; }
set { }
}
public IStreamGeometryImpl Clone()
{
// TODO: Implement
return new StreamGeometryImpl();
}
public Rect GetRenderBounds(double strokeThickness) public Rect GetRenderBounds(double strokeThickness)
{ {
// TODO: Implement // TODO: Implement

45
Perspex.Controls/Shapes/Shape.cs

@ -24,6 +24,8 @@ namespace Perspex.Controls.Shapes
public static readonly PerspexProperty<double> StrokeThicknessProperty = public static readonly PerspexProperty<double> StrokeThicknessProperty =
PerspexProperty.Register<Shape, double>("StrokeThickness"); PerspexProperty.Register<Shape, double>("StrokeThickness");
private Matrix transform = Matrix.Identity;
public abstract Geometry DefiningGeometry public abstract Geometry DefiningGeometry
{ {
get; get;
@ -37,7 +39,18 @@ namespace Perspex.Controls.Shapes
public Geometry RenderedGeometry public Geometry RenderedGeometry
{ {
get { return this.DefiningGeometry; } get
{
var result = this.DefiningGeometry;
if (result != null)
{
result = result.Clone();
result.Transform = new MatrixTransform(this.transform);
}
return result;
}
} }
public Stretch Stretch public Stretch Stretch
@ -60,15 +73,19 @@ namespace Perspex.Controls.Shapes
public override void Render(IDrawingContext context) public override void Render(IDrawingContext context)
{ {
if (this.RenderedGeometry != null) var geometry = this.RenderedGeometry;
if (geometry != null)
{ {
context.DrawGeometry(this.Fill, new Pen(this.Stroke, this.StrokeThickness), this.RenderedGeometry); context.DrawGeometry(this.Fill, new Pen(this.Stroke, this.StrokeThickness), geometry);
} }
} }
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
Rect shapeBounds = this.RenderedGeometry.GetRenderBounds(this.StrokeThickness); Rect shapeBounds = this.DefiningGeometry.GetRenderBounds(this.StrokeThickness);
Size shapeSize = new Size(shapeBounds.Right, shapeBounds.Bottom);
Matrix translate = Matrix.Identity;
double width = this.Width; double width = this.Width;
double height = this.Height; double height = this.Height;
double desiredX = availableSize.Width; double desiredX = availableSize.Width;
@ -76,24 +93,30 @@ namespace Perspex.Controls.Shapes
double sx = 0.0; double sx = 0.0;
double sy = 0.0; double sy = 0.0;
if (this.Stretch != Stretch.None)
{
shapeSize = shapeBounds.Size;
translate = Matrix.Translation(-(Vector)shapeBounds.Position);
}
if (double.IsInfinity(availableSize.Width)) if (double.IsInfinity(availableSize.Width))
{ {
desiredX = shapeBounds.Right; desiredX = shapeSize.Width;
} }
if (double.IsInfinity(availableSize.Height)) if (double.IsInfinity(availableSize.Height))
{ {
desiredY = shapeBounds.Bottom; desiredY = shapeSize.Height;
} }
if (shapeBounds.Width > 0) if (shapeBounds.Width > 0)
{ {
sx = desiredX / shapeBounds.Right; sx = desiredX / shapeBounds.Width;
} }
if (shapeBounds.Height > 0) if (shapeBounds.Height > 0)
{ {
sy = desiredY / shapeBounds.Bottom; sy = desiredY / shapeBounds.Height;
} }
if (double.IsInfinity(availableSize.Width)) if (double.IsInfinity(availableSize.Width))
@ -131,8 +154,10 @@ namespace Perspex.Controls.Shapes
break; break;
} }
double finalX = (width > 0) ? width : shapeBounds.Right * sx; this.transform = translate * Matrix.Scaling(sx, sy);
double finalY = (height > 0) ? height : shapeBounds.Bottom * sy;
double finalX = (width > 0) ? width : shapeSize.Width * sx;
double finalY = (height > 0) ? height : shapeSize.Width * sy;
return new Size(finalX, finalY); return new Size(finalX, finalY);
} }
} }

10
Perspex.SceneGraph/Matrix.cs

@ -106,6 +106,16 @@ namespace Perspex
return new Matrix(cos, sin, -sin, cos, 0, 0); return new Matrix(cos, sin, -sin, cos, 0, 0);
} }
public static Matrix Scaling(double x, double y)
{
return Scaling(new Vector(x, y));
}
public static Matrix Scaling(Vector scale)
{
return new Matrix(scale.X, 0, 0, scale.Y, 0, 0);
}
public static Matrix Translation(Vector v) public static Matrix Translation(Vector v)
{ {
return Translation(v.X, v.Y); return Translation(v.X, v.Y);

22
Perspex.SceneGraph/Media/Geometry.cs

@ -6,10 +6,22 @@
namespace Perspex.Media namespace Perspex.Media
{ {
using System;
using Perspex.Platform; using Perspex.Platform;
public abstract class Geometry public abstract class Geometry : PerspexObject
{ {
public static readonly PerspexProperty<ITransform> TransformProperty =
PerspexProperty.Register<Geometry, ITransform>("Transform");
static Geometry()
{
TransformProperty.Changed.Subscribe(x =>
{
((Geometry)x.Sender).PlatformImpl.Transform = ((ITransform)x.NewValue).Value;
});
}
public abstract Rect Bounds public abstract Rect Bounds
{ {
get; get;
@ -21,6 +33,14 @@ namespace Perspex.Media
protected set; protected set;
} }
public ITransform Transform
{
get { return this.GetValue(TransformProperty); }
set { this.SetValue(TransformProperty, value); }
}
public abstract Geometry Clone();
public Rect GetRenderBounds(double strokeThickness) public Rect GetRenderBounds(double strokeThickness)
{ {
return this.PlatformImpl.GetRenderBounds(strokeThickness); return this.PlatformImpl.GetRenderBounds(strokeThickness);

34
Perspex.SceneGraph/Media/MatrixTransform.cs

@ -0,0 +1,34 @@
// -----------------------------------------------------------------------
// <copyright file="RotateTransform.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
{
public class MatrixTransform : Transform
{
public static readonly PerspexProperty<Matrix> MatrixProperty =
PerspexProperty.Register<MatrixTransform, Matrix>("Matrix", Matrix.Identity);
public MatrixTransform()
{
}
public MatrixTransform(Matrix matrix)
{
this.Matrix = matrix;
}
public Matrix Matrix
{
get { return this.GetValue(MatrixProperty); }
set { this.SetValue(MatrixProperty, value); }
}
public override Matrix Value
{
get { return this.Matrix; }
}
}
}

66
Perspex.SceneGraph/Media/PathMarkupParser.cs

@ -85,7 +85,15 @@ namespace Perspex.Media
this.context.EndFigure(false); this.context.EndFigure(false);
} }
point = ReadPoint(reader); if (command == Command.Move)
{
point = ReadPoint(reader);
}
else
{
point = ReadRelativePoint(reader, point);
}
this.context.BeginFigure(point, true); this.context.BeginFigure(point, true);
openFigure = true; openFigure = true;
break; break;
@ -100,34 +108,34 @@ namespace Perspex.Media
this.context.LineTo(point); this.context.LineTo(point);
break; break;
////case Command.HorizontalLine: case Command.HorizontalLine:
//// point.X = ReadDouble(reader); point = point.WithX(ReadDouble(reader));
//// this.context.LineTo(point, true, false); this.context.LineTo(point);
//// break; break;
////case Command.HorizontalLineRelative: case Command.HorizontalLineRelative:
//// point.X += ReadDouble(reader); point = new Point(point.X + ReadDouble(reader), point.Y);
//// this.context.LineTo(point, true, false); this.context.LineTo(point);
//// break; break;
////case Command.VerticalLine: case Command.VerticalLine:
//// point.Y = ReadDouble(reader); point = point.WithY(ReadDouble(reader));
//// this.context.LineTo(point, true, false); this.context.LineTo(point);
//// break; break;
////case Command.VerticalLineRelative: case Command.VerticalLineRelative:
//// point.Y += ReadDouble(reader); point = new Point(point.X, point.Y + ReadDouble(reader));
//// this.context.LineTo(point, true, false); this.context.LineTo(point);
//// break; break;
////case Command.CubicBezierCurve: case Command.CubicBezierCurve:
////{ {
//// Point point1 = ReadPoint(reader); Point point1 = ReadPoint(reader);
//// Point point2 = ReadPoint(reader); Point point2 = ReadPoint(reader);
//// point = ReadPoint(reader); point = ReadPoint(reader);
//// this.context.BezierTo(point1, point2, point, true, false); this.context.BezierTo(point1, point2, point);
//// break; break;
////} }
case Command.Close: case Command.Close:
this.context.EndFigure(true); this.context.EndFigure(true);

6
Perspex.SceneGraph/Media/RectangleGeometry.cs

@ -6,6 +6,7 @@
namespace Perspex.Media namespace Perspex.Media
{ {
using System;
using Perspex.Platform; using Perspex.Platform;
using Splat; using Splat;
@ -32,5 +33,10 @@ namespace Perspex.Media
{ {
get { return this.PlatformImpl.Bounds; } get { return this.PlatformImpl.Bounds; }
} }
public override Geometry Clone()
{
return new RectangleGeometry(this.Bounds);
}
} }
} }

11
Perspex.SceneGraph/Media/StreamGeometry.cs

@ -6,6 +6,7 @@
namespace Perspex.Media namespace Perspex.Media
{ {
using System;
using Perspex.Platform; using Perspex.Platform;
using Splat; using Splat;
@ -34,9 +35,19 @@ namespace Perspex.Media
} }
} }
public override Geometry Clone()
{
return new StreamGeometry(((IStreamGeometryImpl)this.PlatformImpl).Clone());
}
public StreamGeometryContext Open() public StreamGeometryContext Open()
{ {
return new StreamGeometryContext(((IStreamGeometryImpl)this.PlatformImpl).Open()); return new StreamGeometryContext(((IStreamGeometryImpl)this.PlatformImpl).Open());
} }
private StreamGeometry(IGeometryImpl impl)
{
this.PlatformImpl = impl;
}
} }
} }

5
Perspex.SceneGraph/Media/StreamGeometryContext.cs

@ -23,6 +23,11 @@ namespace Perspex.Media
this.impl.BeginFigure(startPoint, isFilled); this.impl.BeginFigure(startPoint, isFilled);
} }
public void BezierTo(Point point1, Point point2, Point point3)
{
this.impl.BezierTo(point1, point2, point3);
}
public void LineTo(Point point) public void LineTo(Point point)
{ {
this.impl.LineTo(point); this.impl.LineTo(point);

1
Perspex.SceneGraph/Perspex.SceneGraph.csproj

@ -59,6 +59,7 @@
<Compile Include="Media\PathMarkupParser.cs" /> <Compile Include="Media\PathMarkupParser.cs" />
<Compile Include="Media\Pen.cs" /> <Compile Include="Media\Pen.cs" />
<Compile Include="Media\RectangleGeometry.cs" /> <Compile Include="Media\RectangleGeometry.cs" />
<Compile Include="Media\MatrixTransform.cs" />
<Compile Include="Media\RotateTransform.cs" /> <Compile Include="Media\RotateTransform.cs" />
<Compile Include="Media\SolidColorBrush.cs" /> <Compile Include="Media\SolidColorBrush.cs" />
<Compile Include="Media\StreamGeometry.cs" /> <Compile Include="Media\StreamGeometry.cs" />

2
Perspex.SceneGraph/Platform/IGeometryImpl.cs

@ -10,6 +10,8 @@ namespace Perspex.Platform
{ {
Rect Bounds { get; } Rect Bounds { get; }
Matrix Transform { get; set; }
Rect GetRenderBounds(double strokeThickness); Rect GetRenderBounds(double strokeThickness);
} }
} }

2
Perspex.SceneGraph/Platform/IStreamGeometryContextImpl.cs

@ -12,6 +12,8 @@ namespace Perspex.Platform
{ {
void BeginFigure(Point startPoint, bool isFilled); void BeginFigure(Point startPoint, bool isFilled);
void BezierTo(Point point1, Point point2, Point point3);
void LineTo(Point point); void LineTo(Point point);
void EndFigure(bool isClosed); void EndFigure(bool isClosed);

2
Perspex.SceneGraph/Platform/IStreamGeometryImpl.cs

@ -8,6 +8,8 @@ namespace Perspex.Platform
{ {
public interface IStreamGeometryImpl : IGeometryImpl public interface IStreamGeometryImpl : IGeometryImpl
{ {
IStreamGeometryImpl Clone();
IStreamGeometryContextImpl Open(); IStreamGeometryContextImpl Open();
} }
} }

10
Perspex.SceneGraph/Point.cs

@ -124,5 +124,15 @@ namespace Perspex
{ {
return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", this.x, this.y); return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", this.x, this.y);
} }
public Point WithX(double x)
{
return new Point(x, this.y);
}
public Point WithY(double y)
{
return new Point(this.x, y);
}
} }
} }

BIN
TestFiles/Direct2D1/Shapes/Path/Path_Tick_Scaled.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

24
Windows/Perspex.Direct2D1.RenderTests/Shapes/PathTests.cs

@ -41,5 +41,29 @@ namespace Perspex.Direct2D1.RenderTests.Shapes
this.RenderToFile(target); this.RenderToFile(target);
this.CompareImages(); this.CompareImages();
} }
[TestMethod]
public void Path_Tick_Scaled()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Content = new Path
{
Fill = Brushes.Gray,
Stroke = Brushes.Red,
StrokeThickness = 2,
Stretch = Stretch.Uniform,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = StreamGeometry.Parse("M 1145.607177734375,430 C1145.607177734375,430 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1138,434.5538330078125 1138,434.5538330078125 1138,434.5538330078125 1141.482177734375,438 1141.482177734375,438 1141.482177734375,438 1141.96875,437.9375 1141.96875,437.9375 1141.96875,437.9375 1147,431.34619140625 1147,431.34619140625 1147,431.34619140625 1145.607177734375,430 1145.607177734375,430 z"),
}
};
this.RenderToFile(target);
this.CompareImages();
}
} }
} }

44
Windows/Perspex.Direct2D1/Media/GeometryImpl.cs

@ -6,21 +6,57 @@
namespace Perspex.Direct2D1.Media namespace Perspex.Direct2D1.Media
{ {
using System;
using Perspex.Media;
using Perspex.Platform; using Perspex.Platform;
using SharpDX.Direct2D1;
using Splat;
public abstract class GeometryImpl : IGeometryImpl public abstract class GeometryImpl : IGeometryImpl
{ {
private TransformedGeometry transformed;
public abstract Rect Bounds public abstract Rect Bounds
{ {
get; get;
} }
public SharpDX.Direct2D1.Geometry Geometry public abstract Geometry DefiningGeometry
{ {
get; get;
protected set; }
public Geometry Geometry
{
get { return this.transformed ?? this.DefiningGeometry; }
}
public Matrix Transform
{
get
{
return this.transformed != null ?
this.transformed.Transform.ToPerspex() :
Matrix.Identity;
}
set
{
if (value != this.Transform)
{
if (this.transformed != null)
{
this.transformed.Dispose();
}
if (!value.IsIdentity)
{
Factory factory = Locator.Current.GetService<Factory>();
this.transformed = new TransformedGeometry(
factory,
this.DefiningGeometry,
value.ToDirect2D());
}
}
}
} }
public abstract Rect GetRenderBounds(double strokeThickness); public abstract Rect GetRenderBounds(double strokeThickness);

10
Windows/Perspex.Direct2D1/Media/StreamGeometryContextImpl.cs

@ -23,6 +23,16 @@ namespace Perspex.Direct2D1.Media
this.sink.BeginFigure(startPoint.ToSharpDX(), isFilled ? FigureBegin.Filled : FigureBegin.Hollow); this.sink.BeginFigure(startPoint.ToSharpDX(), isFilled ? FigureBegin.Filled : FigureBegin.Hollow);
} }
public void BezierTo(Point point1, Point point2, Point point3)
{
this.sink.AddBezier(new BezierSegment
{
Point1 = point1.ToSharpDX(),
Point2 = point2.ToSharpDX(),
Point3 = point3.ToSharpDX(),
});
}
public void LineTo(Point point) public void LineTo(Point point)
{ {
this.sink.AddLine(point.ToSharpDX()); this.sink.AddLine(point.ToSharpDX());

33
Windows/Perspex.Direct2D1/Media/StreamGeometryImpl.cs

@ -9,33 +9,54 @@ namespace Perspex.Direct2D1.Media
using System; using System;
using Perspex.Media; using Perspex.Media;
using Perspex.Platform; using Perspex.Platform;
using SharpDX;
using SharpDX.Direct2D1; using SharpDX.Direct2D1;
using Splat; using Splat;
using D2DGeometry = SharpDX.Direct2D1.Geometry;
public class StreamGeometryImpl : GeometryImpl, IStreamGeometryImpl public class StreamGeometryImpl : GeometryImpl, IStreamGeometryImpl
{ {
private PathGeometry geometry; private PathGeometry path;
public StreamGeometryImpl() public StreamGeometryImpl()
{ {
Factory factory = Locator.Current.GetService<Factory>(); Factory factory = Locator.Current.GetService<Factory>();
this.geometry = new PathGeometry(factory); this.path = new PathGeometry(factory);
this.Geometry = this.geometry;
} }
public override Rect Bounds public override Rect Bounds
{ {
get { return this.geometry.GetBounds().ToPerspex(); } get { return this.path.GetBounds().ToPerspex(); }
}
public override D2DGeometry DefiningGeometry
{
get { return this.path; }
}
public IStreamGeometryImpl Clone()
{
Factory factory = Locator.Current.GetService<Factory>();
var result = new PathGeometry(factory);
var sink = result.Open();
this.path.Stream(sink);
sink.Close();
return new StreamGeometryImpl(result);
} }
public override Rect GetRenderBounds(double strokeThickness) public override Rect GetRenderBounds(double strokeThickness)
{ {
return this.geometry.GetWidenedBounds((float)strokeThickness).ToPerspex(); return this.path.GetWidenedBounds((float)strokeThickness).ToPerspex();
} }
public IStreamGeometryContextImpl Open() public IStreamGeometryContextImpl Open()
{ {
return new StreamGeometryContextImpl(this.geometry.Open()); return new StreamGeometryContextImpl(this.path.Open());
}
protected StreamGeometryImpl(PathGeometry geometry)
{
this.path = geometry;
} }
} }
} }

Loading…
Cancel
Save