committed by
GitHub
30 changed files with 409 additions and 54 deletions
@ -0,0 +1,53 @@ |
|||||
|
using System; |
||||
|
using Avalonia; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Media; |
||||
|
using Avalonia.Rendering.SceneGraph; |
||||
|
using Avalonia.Threading; |
||||
|
|
||||
|
namespace RenderDemo.Controls |
||||
|
{ |
||||
|
public class LineBoundsDemoControl : Control |
||||
|
{ |
||||
|
static LineBoundsDemoControl() |
||||
|
{ |
||||
|
AffectsRender<LineBoundsDemoControl>(AngleProperty); |
||||
|
} |
||||
|
|
||||
|
public LineBoundsDemoControl() |
||||
|
{ |
||||
|
var timer = new DispatcherTimer(); |
||||
|
timer.Interval = TimeSpan.FromSeconds(1 / 60); |
||||
|
timer.Tick += (sender, e) => Angle += Math.PI / 360; |
||||
|
timer.Start(); |
||||
|
} |
||||
|
|
||||
|
public static readonly StyledProperty<double> AngleProperty = |
||||
|
AvaloniaProperty.Register<LineBoundsDemoControl, double>(nameof(Angle)); |
||||
|
|
||||
|
public double Angle |
||||
|
{ |
||||
|
get => GetValue(AngleProperty); |
||||
|
set => SetValue(AngleProperty, value); |
||||
|
} |
||||
|
|
||||
|
public override void Render(DrawingContext drawingContext) |
||||
|
{ |
||||
|
var lineLength = Math.Sqrt((100 * 100) + (100 * 100)); |
||||
|
|
||||
|
var diffX = LineBoundsHelper.CalculateAdjSide(Angle, lineLength); |
||||
|
var diffY = LineBoundsHelper.CalculateOppSide(Angle, lineLength); |
||||
|
|
||||
|
|
||||
|
var p1 = new Point(200, 200); |
||||
|
var p2 = new Point(p1.X + diffX, p1.Y + diffY); |
||||
|
|
||||
|
var pen = new Pen(Brushes.Green, 20, lineCap: PenLineCap.Square); |
||||
|
var boundPen = new Pen(Brushes.Black); |
||||
|
|
||||
|
drawingContext.DrawLine(pen, p1, p2); |
||||
|
|
||||
|
drawingContext.DrawRectangle(boundPen, LineBoundsHelper.CalculateBounds(p1, p2, pen)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
<UserControl xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" |
||||
|
xmlns:controls="clr-namespace:RenderDemo.Controls" |
||||
|
x:Class="RenderDemo.Pages.LineBoundsPage"> |
||||
|
<controls:LineBoundsDemoControl /> |
||||
|
</UserControl> |
||||
@ -0,0 +1,19 @@ |
|||||
|
using Avalonia; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
|
||||
|
namespace RenderDemo.Pages |
||||
|
{ |
||||
|
public class LineBoundsPage : UserControl |
||||
|
{ |
||||
|
public LineBoundsPage() |
||||
|
{ |
||||
|
this.InitializeComponent(); |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace Avalonia.Rendering.SceneGraph |
||||
|
{ |
||||
|
internal static class LineBoundsHelper |
||||
|
{ |
||||
|
private static double CalculateAngle(Point p1, Point p2) |
||||
|
{ |
||||
|
var xDiff = p2.X - p1.X; |
||||
|
var yDiff = p2.Y - p1.Y; |
||||
|
|
||||
|
return Math.Atan2(yDiff, xDiff); |
||||
|
} |
||||
|
|
||||
|
internal static double CalculateOppSide(double angle, double hyp) |
||||
|
{ |
||||
|
return Math.Sin(angle) * hyp; |
||||
|
} |
||||
|
|
||||
|
internal static double CalculateAdjSide(double angle, double hyp) |
||||
|
{ |
||||
|
return Math.Cos(angle) * hyp; |
||||
|
} |
||||
|
|
||||
|
private static (Point p1, Point p2) TranslatePointsAlongTangent(Point p1, Point p2, double angle, double distance) |
||||
|
{ |
||||
|
var xDiff = CalculateOppSide(angle, distance); |
||||
|
var yDiff = CalculateAdjSide(angle, distance); |
||||
|
|
||||
|
var c1 = new Point(p1.X + xDiff, p1.Y - yDiff); |
||||
|
var c2 = new Point(p1.X - xDiff, p1.Y + yDiff); |
||||
|
|
||||
|
var c3 = new Point(p2.X + xDiff, p2.Y - yDiff); |
||||
|
var c4 = new Point(p2.X - xDiff, p2.Y + yDiff); |
||||
|
|
||||
|
var minX = Math.Min(c1.X, Math.Min(c2.X, Math.Min(c3.X, c4.X))); |
||||
|
var minY = Math.Min(c1.Y, Math.Min(c2.Y, Math.Min(c3.Y, c4.Y))); |
||||
|
var maxX = Math.Max(c1.X, Math.Max(c2.X, Math.Max(c3.X, c4.X))); |
||||
|
var maxY = Math.Max(c1.Y, Math.Max(c2.Y, Math.Max(c3.Y, c4.Y))); |
||||
|
|
||||
|
return (new Point(minX, minY), new Point(maxX, maxY)); |
||||
|
} |
||||
|
|
||||
|
private static Rect CalculateBounds(Point p1, Point p2, double thickness, double angleToCorner) |
||||
|
{ |
||||
|
var pts = TranslatePointsAlongTangent(p1, p2, angleToCorner, thickness / 2); |
||||
|
|
||||
|
return new Rect(pts.p1, pts.p2); |
||||
|
} |
||||
|
|
||||
|
public static Rect CalculateBounds(Point p1, Point p2, IPen p) |
||||
|
{ |
||||
|
var radians = CalculateAngle(p1, p2); |
||||
|
|
||||
|
if (p.LineCap != PenLineCap.Flat) |
||||
|
{ |
||||
|
var pts = TranslatePointsAlongTangent(p1, p2, radians - Math.PI / 2, p.Thickness / 2); |
||||
|
|
||||
|
return CalculateBounds(pts.p1, pts.p2, p.Thickness, radians); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return CalculateBounds(p1, p2, p.Thickness, radians); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Visuals.UnitTests.Media |
||||
|
{ |
||||
|
public class PixelRectTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void FromRect_Snaps_To_Device_Pixels() |
||||
|
{ |
||||
|
var rect = new Rect(189, 189, 26, 164); |
||||
|
var result = PixelRect.FromRect(rect, 1.5); |
||||
|
|
||||
|
Assert.Equal(new PixelRect(283, 283, 40, 247), result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void FromRect_Vector_Snaps_To_Device_Pixels() |
||||
|
{ |
||||
|
var rect = new Rect(189, 189, 26, 164); |
||||
|
var result = PixelRect.FromRect(rect, new Vector(1.5, 1.5)); |
||||
|
|
||||
|
Assert.Equal(new PixelRect(283, 283, 40, 247), result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void FromRectWithDpi_Snaps_To_Device_Pixels() |
||||
|
{ |
||||
|
var rect = new Rect(189, 189, 26, 164); |
||||
|
var result = PixelRect.FromRectWithDpi(rect, 144); |
||||
|
|
||||
|
Assert.Equal(new PixelRect(283, 283, 40, 247), result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void FromRectWithDpi_Vector_Snaps_To_Device_Pixels() |
||||
|
{ |
||||
|
var rect = new Rect(189, 189, 26, 164); |
||||
|
var result = PixelRect.FromRectWithDpi(rect, new Vector(144, 144)); |
||||
|
|
||||
|
Assert.Equal(new PixelRect(283, 283, 40, 247), result); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue