Files
Shrlalgo.RvKits/WPFluent/Layout/Border.cs

230 lines
7.5 KiB
C#

using System;
using System.Windows;
using System.Windows.Media;
namespace WPFluent.Layout;
public class Border : System.Windows.Controls.Border
{
protected override Geometry? GetLayoutClip(Size layoutSlotSize)
{
if (!ClipToBounds)
{
return base.GetLayoutClip(layoutSlotSize);
}
var borderThickness = BorderThickness;
var cornerRadius = CornerRadius;
var renderSize = RenderSize;
if (renderSize.Width <= 0 || renderSize.Height <= 0)
{
return null;
}
var rect = new Rect(0, 0, renderSize.Width, renderSize.Height);
var radii = new Radii(cornerRadius, borderThickness, true);
var layoutGeometry = new StreamGeometry();
using StreamGeometryContext ctx = layoutGeometry.Open();
GenerateGeometry(ctx, rect, radii);
layoutGeometry.Freeze();
return layoutGeometry;
}
internal static void GenerateGeometry(StreamGeometryContext ctx, Rect rect, Radii radii)
{
//
// compute the coordinates of the key points
//
Point topLeft = new(radii.LeftTop, 0);
Point topRight = new(rect.Width - radii.RightTop, 0);
Point rightTop = new(rect.Width, radii.TopRight);
Point rightBottom = new(rect.Width, rect.Height - radii.BottomRight);
Point bottomRight = new(rect.Width - radii.RightBottom, rect.Height);
Point bottomLeft = new(radii.LeftBottom, rect.Height);
Point leftBottom = new(0, rect.Height - radii.BottomLeft);
Point leftTop = new(0, radii.TopLeft);
//
// check key points for overlap and resolve by partitioning radii according to
// the percentage of each one.
//
// top edge is handled here
if (topLeft.X > topRight.X)
{
double v = (radii.LeftTop) / (radii.LeftTop + radii.RightTop) * rect.Width;
topLeft.X = v;
topRight.X = v;
}
// right edge
if (rightTop.Y > rightBottom.Y)
{
double v = (radii.TopRight) / (radii.TopRight + radii.BottomRight) * rect.Height;
rightTop.Y = v;
rightBottom.Y = v;
}
// bottom edge
if (bottomRight.X < bottomLeft.X)
{
double v = (radii.LeftBottom) / (radii.LeftBottom + radii.RightBottom) * rect.Width;
bottomRight.X = v;
bottomLeft.X = v;
}
// left edge
if (leftBottom.Y < leftTop.Y)
{
double v = (radii.TopLeft) / (radii.TopLeft + radii.BottomLeft) * rect.Height;
leftBottom.Y = v;
leftTop.Y = v;
}
//
// add on offsets
//
Vector offset = new(rect.TopLeft.X, rect.TopLeft.Y);
topLeft += offset;
topRight += offset;
rightTop += offset;
rightBottom += offset;
bottomRight += offset;
bottomLeft += offset;
leftBottom += offset;
leftTop += offset;
//
// create the border geometry
//
ctx.BeginFigure(topLeft, true /* is filled */, true /* is closed */);
// Top line
ctx.LineTo(topRight, true /* is stroked */, false /* is smooth join */);
// Upper-right corner
double radiusX = rect.TopRight.X - topRight.X;
double radiusY = rightTop.Y - rect.TopRight.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(rightTop, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
// Right line
ctx.LineTo(rightBottom, true /* is stroked */, false /* is smooth join */);
// Lower-right corner
radiusX = rect.BottomRight.X - bottomRight.X;
radiusY = rect.BottomRight.Y - rightBottom.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(bottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
// Bottom line
ctx.LineTo(bottomLeft, true /* is stroked */, false /* is smooth join */);
// Lower-left corner
radiusX = bottomLeft.X - rect.BottomLeft.X;
radiusY = rect.BottomLeft.Y - leftBottom.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(leftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
// Left line
ctx.LineTo(leftTop, true /* is stroked */, false /* is smooth join */);
// Upper-left corner
radiusX = topLeft.X - rect.TopLeft.X;
radiusY = leftTop.Y - rect.TopLeft.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(topLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
}
internal struct Radii
{
internal Radii(CornerRadius radii, Thickness borders, bool outer)
{
double left = 0.5 * borders.Left;
double top = 0.5 * borders.Top;
double right = 0.5 * borders.Right;
double bottom = 0.5 * borders.Bottom;
if (outer)
{
if (MathHelper.IsZero(radii.TopLeft))
{
LeftTop = TopLeft = 0.0;
}
else
{
LeftTop = radii.TopLeft + left;
TopLeft = radii.TopLeft + top;
}
if (MathHelper.IsZero(radii.TopRight))
{
TopRight = RightTop = 0.0;
}
else
{
TopRight = radii.TopRight + top;
RightTop = radii.TopRight + right;
}
if (MathHelper.IsZero(radii.BottomRight))
{
RightBottom = BottomRight = 0.0;
}
else
{
RightBottom = radii.BottomRight + right;
BottomRight = radii.BottomRight + bottom;
}
if (MathHelper.IsZero(radii.BottomLeft))
{
BottomLeft = LeftBottom = 0.0;
}
else
{
BottomLeft = radii.BottomLeft + bottom;
LeftBottom = radii.BottomLeft + left;
}
}
else
{
LeftTop = Math.Max(0.0, radii.TopLeft - left);
TopLeft = Math.Max(0.0, radii.TopLeft - top);
TopRight = Math.Max(0.0, radii.TopRight - top);
RightTop = Math.Max(0.0, radii.TopRight - right);
RightBottom = Math.Max(0.0, radii.BottomRight - right);
BottomRight = Math.Max(0.0, radii.BottomRight - bottom);
BottomLeft = Math.Max(0.0, radii.BottomLeft - bottom);
LeftBottom = Math.Max(0.0, radii.BottomLeft - left);
}
}
internal double LeftTop;
internal double TopLeft;
internal double TopRight;
internal double RightTop;
internal double RightBottom;
internal double BottomRight;
internal double BottomLeft;
internal double LeftBottom;
}
}
file static class MathHelper
{
internal const double DBL_EPSILON = 2.2204460492503131e-016;
public static bool IsZero(double value) => Math.Abs(value) < 10.0 * DBL_EPSILON;
}