Files
kniip/src/Kniip/Editor/Annotations/FigureAnnotation.cs
T
2026-11-10 15:33:49 +01:00

107 lines
3.3 KiB
C#

using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
namespace Kniip.Editor.Annotations;
/// <summary>Een vorm uit het vormengereedschap (driehoek, ster, hart, ...), geschaald naar zijn kader.</summary>
public sealed class FigureAnnotation : RectAnnotation
{
public bool FlipX { get; set; }
public bool FlipY { get; set; }
public override ToolKind Kind => ToolKind.Shape;
public override bool CanRotate => true;
public override Rect Frame => Rect;
public override Rect Bounds => RotatedBounds(Rect);
private bool IsOpen => ShapeGeometries.IsOpen(Style.ShapeKind);
/// <summary>De vorm in beeldcoördinaten; via de geometrietransformatie, zodat de lijndikte niet meeschaalt.</summary>
private Geometry BuildGeometry()
{
Geometry source = ShapeGeometries.For(Style.ShapeKind);
Rect b = source.Bounds;
Rect r = Rect;
double cx = r.Left + r.Width / 2, cy = r.Top + r.Height / 2;
var m = Matrix.Identity;
m.Translate(-(b.Left + b.Width / 2), -(b.Top + b.Height / 2));
m.Scale(r.Width / b.Width * (FlipX ? -1 : 1), r.Height / b.Height * (FlipY ? -1 : 1));
m.Translate(cx, cy);
m.RotateAt(Angle, cx, cy);
Geometry geometry = source.Clone();
geometry.Transform = new MatrixTransform(m);
geometry.Freeze();
return geometry;
}
public override void Render(DrawingContext dc, EditorDocument document)
{
Rect r = Rect;
if (r.Width < 1 && r.Height < 1)
{
return;
}
Brush? fill = IsOpen ? null : RenderHelpers.BrushOrNull(Style.Fill);
RenderHelpers.DrawWithShadow(dc, BuildGeometry(), fill,
RenderHelpers.CreatePen(Style.Stroke, Style.Thickness, Style.Dash), Style.Shadow);
}
public override bool HitTest(Point point, double tolerance)
{
Geometry geometry = BuildGeometry();
if (!IsOpen && geometry.FillContains(point))
{
return true;
}
var pen = new Pen(Brushes.Black, Style.Thickness + 2 * tolerance);
return geometry.StrokeContains(pen, point);
}
public override IReadOnlyList<Point> GetHandles()
{
Matrix m = RotationMatrix;
return base.GetHandles().Select(p => m.Transform(p)).ToArray();
}
/// <summary>Rekent de muis terug naar het ongedraaide kader; daarna wordt het kader zo verschoven dat de tegenoverliggende greep op zijn plek blijft.</summary>
public override void MoveHandle(int index, Point p, bool constrain)
{
Rect o = DragOrigin;
var c = new Point(o.Left + o.Width / 2, o.Top + o.Height / 2);
Matrix toLocal = Matrix.Identity;
toLocal.RotateAt(-Angle, c.X, c.Y);
base.MoveHandle(index, toLocal.Transform(p), constrain);
Rect r = Rect;
var rc = new Point(r.Left + r.Width / 2, r.Top + r.Height / 2);
Matrix toWorld = Matrix.Identity;
toWorld.RotateAt(Angle, c.X, c.Y);
Rect = Rect.Offset(r, toWorld.Transform(rc) - rc);
}
public override bool CanFlip => true;
public override void Flip(bool horizontal)
{
if (horizontal)
{
FlipX = !FlipX;
}
else
{
FlipY = !FlipY;
}
Angle = NormalizeAngle(-Angle);
}
}