Avalonia 创建拼图块图形

Avalonia 创建拼图块图形

零、前言

因为想要做一个拼图游戏,如何自绘一个矢量的拼图成为了一个难题,但是还是手搓出来了,实现代码我们放在最后,在前面我们先介绍一下用法。

一、快速开始

你可以为你的 Path.Data 属性提供 JigsawGeometry(代码在文章最后)实例,实现拼图的显示。

示例代码如下:

// 主体的尺寸
var mainSize = 100;

// 拼图块四个方向的情况,HoleType 枚举有三个状态:In Out Flat 分别表示的是该拼图块内凹、外凸与平边缘。
var jigsawConfig = new JigsawConfig(
    left: HoleType.Out, 
    top: HoleType.Out,
    right: HoleType.In,
    bottom: HoleType.Flat);

// 这样就可以创建出一个拼图图形了。
var geometry = new JigsawGeometry(mainSize, jigsawConfig);

PART_Path.Data = geometry;

二、拼图块的属性

我们封装了一些属性,包括拼图块黄色区域表示的实际尺寸、天蓝色区域表示的主要尺寸,以及拼图块的中心。


你可以通过以下代码搭建一个测试场景:

    <Grid ColumnDefinitions="auto,1*">
        <Button
            Name="PART_Button"
            Click="Button_Click"
            Content="点击" />
        <Canvas Grid.Column="1" ClipToBounds="True">

            <Rectangle Name="PART_FullRect" Fill="Yellow" />

            <Rectangle Name="PART_MainRect" Fill="Aqua" />

            <Path
                Name="PART_Path"
                Stroke="Black"
                StrokeThickness="2" />

            <Ellipse
                Name="PART_Point"
                Width="20"
                Height="20"
                Fill="Red"
                RenderTransformOrigin="0.5,0.5">
                <Ellipse.RenderTransform>
                    <TranslateTransform X="-10" Y="-10" />
                </Ellipse.RenderTransform>
            </Ellipse>
        </Canvas>

    </Grid>
    private List<JigsawConfig> _jigsawConfigs;
    private int i = 0;


    protected override void OnLoaded(RoutedEventArgs e)
    {
        base.OnLoaded(e);

        _jigsawConfigs = new List<JigsawConfig>();

        var values = Enum.GetValues(typeof(HoleType));

        foreach (HoleType left in values)
        {
            foreach (HoleType top in values)
            {
                foreach (HoleType right in values)
                {
                    foreach (HoleType bottom in values)
                    {
                        _jigsawConfigs.Add(new JigsawConfig(left, top, right, bottom));
                    }
                }
            }
        }
    }

    private void Button_Click(object? sender, RoutedEventArgs e)
    {
        var mainSize = 100;

        var jigsawConfig = _jigsawConfigs[i];
        i = (i + 1) % _jigsawConfigs.Count;

        var geometry = new JigsawGeometry(mainSize, jigsawConfig);

        PART_Path.Data = geometry;

        // 属性可视化
        {
            Canvas.SetLeft(PART_Point, geometry.MainCenter.X);
            Canvas.SetTop(PART_Point, geometry.MainCenter.Y);

            PART_FullRect.Width = geometry.FullSize.Width;
            PART_FullRect.Height = geometry.FullSize.Height;

            PART_MainRect.Width = geometry.MainRect.Width;
            PART_MainRect.Height = geometry.MainRect.Height;
            Canvas.SetLeft(PART_MainRect, geometry.MainRect.X);
            Canvas.SetTop(PART_MainRect, geometry.MainRect.Y);
        }

    }

三、核心代码

我们的代码实现会很长,具体就会是下面这样:

using Avalonia;
using Avalonia.Media;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AvaloniaPlayground.Controls
{
    /// <summary>
    /// 拼图角的方向。
    /// </summary>
    public enum HoleType
    {
        In, Out, Flat
    }

    /// <summary>
    /// 单个拼图块的具体配置信息。
    /// </summary>
    public class JigsawConfig
    {
        public HoleType Left { get; set; }
        public HoleType Top { get; set; }
        public HoleType Right { get; set; }
        public HoleType Bottom { get; set; }

        public JigsawConfig() { }

        public JigsawConfig(HoleType left, HoleType top, HoleType right, HoleType bottom)
        {
            Left = left;
            Top = top;
            Right = right;
            Bottom = bottom;
        }

        public JigsawConfig(JigsawConfig other)
        {
            Left = other.Left;
            Top = other.Top;
            Right = other.Right;
            Bottom = other.Bottom;
        }

        public static JigsawConfig Parse(string s)
        {
            var parts = s.Split(',', StringSplitOptions.RemoveEmptyEntries);
            return new JigsawConfig(Enum.Parse<HoleType>(parts[0], true), Enum.Parse<HoleType>(parts[1], true), Enum.Parse<HoleType>(parts[2], true), Enum.Parse<HoleType>(parts[3], true));
        }
    }

    /// <summary>
    /// 拼图块。
    /// </summary>
    public partial class JigsawGeometry : PathGeometry
    {
        #region props

        /// <summary>
        /// 主中心。
        /// </summary>
        public Point MainCenter { get; private set; }

        /// <summary>
        /// 主尺寸。
        /// </summary>
        public double MainSize { get; private set; }

        /// <summary>
        /// 全尺寸。
        /// </summary>
        public Size FullSize { get; private set; }

        /// <summary>
        /// 主体矩阵。
        /// </summary>
        public Rect MainRect { get; private set; }

        /// <summary>
        /// 图形的配置信息。
        /// </summary>
        public JigsawConfig JigsawConfig { get; private set; }

        /// <summary>
        /// 连接处的尺寸。
        /// </summary>
        public double Outter { get; private set; }

        #endregion

        #region ctors

        /// <summary>
        /// 拼图几何图形的构造函数。
        /// </summary>
        /// <param name="mainSize">拼图块主尺寸,不包括连接处的外围尺寸。</param>
        /// <param name="jigsawConfig">该拼图块的外观。</param>
        public JigsawGeometry(double mainSize, JigsawConfig jigsawConfig)
        {
            JigsawConfig = jigsawConfig;
            MainSize = mainSize;
            Outter = GetOutterFromMainSize(mainSize);
            Build(mainSize, jigsawConfig);
            CreateMainCenterAndRect(mainSize, jigsawConfig);
            CreateFullSize(mainSize, jigsawConfig);
        }
        #endregion

        #region methods

        /// <summary>
        /// 创建四个方向的边缘和连接处曲线,核心是 CreateSegmentFromJigsawShapeData。
        /// </summary>
        /// <param name="mainSize"></param>
        /// <param name="jigsawConfig"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Build(double mainSize, JigsawConfig jigsawConfig)
        {
            var data = new FullConfigJigsawShapeData(mainSize, jigsawConfig);
            var figure = new PathFigure();
            figure.IsClosed = true;
            figure.StartPoint = data.StartPoint;

            for (int i = 0; i < data.JigsawPoints.Count - 1; i++)
            {
                var segment = CreateSegmentFromJigsawShapeData(data, i);
                if (segment is null) throw new NotImplementedException();

                figure!.Segments!.Add(segment);
            }
            if (Figures is null) Figures = new PathFigures();
            Figures.Add(figure);
        }

        private void CreateMainCenterAndRect(double mainSize, JigsawConfig jigsawConfig)
        {
            var offx = 0d;
            var offy = 0d;
            var x = mainSize / 2;
            var y = mainSize / 2;
            var outter = GetOutterFromMainSize(mainSize);
            if (jigsawConfig.Left == HoleType.Out) offx = outter + offx;
            if (jigsawConfig.Top == HoleType.Out) offy = outter + offy;

            MainCenter = new Point(x + offx, y + offy);
            MainRect = new Rect(offx, offy, mainSize, mainSize);
        }

        private void CreateFullSize(double mainSize, JigsawConfig jigsawConfig)
        {
            var width = mainSize;
            var height = mainSize;
            var outter = GetOutterFromMainSize(mainSize);

            if (jigsawConfig.Left == HoleType.Out) width = outter + width;
            if (jigsawConfig.Right == HoleType.Out) width = outter + width;
            if (jigsawConfig.Top == HoleType.Out) height = outter + height;
            if (jigsawConfig.Bottom == HoleType.Out) height = outter + height;

            FullSize = new Size(width, height);
        }

        private PathSegment CreateSegmentFromJigsawShapeData(IJigsawShapeData data, int index)
        {
            if (index < 0) return null;
            if (index >= data.JigsawPoints.Count) return null;
            var jigsawPoint1 = data.JigsawPoints[index];
            var jigsawPoint2 = data.JigsawPoints[index + 1];

            if (jigsawPoint1.Type == JigsawPointType.Line)
            {
                return new LineSegment() { Point = jigsawPoint2.Point };
            }
            else if (jigsawPoint1.Type == JigsawPointType.Arc)
            {
                return new ArcSegment()
                {
                    IsLargeArc = jigsawPoint1.IsLargeArc,
                    Point = jigsawPoint2.Point,
                    Size = jigsawPoint1.Size,
                    SweepDirection = jigsawPoint1.SweepDirection,
                    RotationAngle = jigsawPoint1.ArcAngle
                };
            }
            return null;
        }
        #endregion


    }

    public partial class JigsawGeometry
    {
        private enum JigsawPointType
        {
            Line, Arc, Point
        }

        private class JigsawPoint
        {
            public JigsawPointType Type;
            public Point Point; // 实际坐标
            public double ArcAngle;
            public Size Size;
            public SweepDirection SweepDirection;
            public bool IsLargeArc = false;

            public static JigsawPoint CreateLine(Point point) => new JigsawPoint() { Point = point, Type = JigsawPointType.Line };
            public static JigsawPoint CreateArc(Point point, Size size, double arcAngle, SweepDirection sweepDirection, bool isLargeArc) =>
                new JigsawPoint()
                {
                    Point = point,
                    Type = JigsawPointType.Arc,
                    Size = size,
                    ArcAngle = arcAngle,
                    SweepDirection = sweepDirection,
                    IsLargeArc = isLargeArc
                };

            public static JigsawPoint CreatePoint(Point point) => new JigsawPoint() { Point = point, Type = JigsawPointType.Point };
        }

        private interface IJigsawShapeData
        {
            Size LargeSize { get; }
            Size SmallSize { get; }
            List<JigsawPoint> JigsawPoints { get; }
            Point StartPoint { get; }

            double Extend { get; }
        }

        private class FullConfigJigsawShapeData : IJigsawShapeData
        {
            #region statics

            ///<summary>从包含延伸的全尺寸计算单个延伸尺寸</summary>
            public static double GetOutterFromFullSize(double fullSize) => fullSize / 6;

            ///<summary>计算</summary>
            public static double GetFullSize(double mainSize) => mainSize / 2 * 3;

            ///<summary>从拼图体尺寸计算延伸的尺寸</summary>
            public static double GetOutterFromMainSize(double mainSize) => mainSize / 4;

            #endregion

            #region fields

            double _largeRadius;
            double _theta;
            double _majorDistance;
            double _mainSize;
            double _extend;
            double _half;
            Size _smallSize;
            double _borderPart;

            #endregion

            #region props

            public Size LargeSize { get; private set; }
            public Size SmallSize { get; private set; }
            public List<JigsawPoint> JigsawPoints { get; private set; }
            public Point StartPoint { get; private set; }

            public JigsawConfig JigsawConfig { get; private set; }
            public double Extend { get; private set; }
            #endregion

            #region ctors 

            public FullConfigJigsawShapeData(double mainSize, JigsawConfig jigsawConfig)
            {
                var extend = GetOutterFromMainSize(mainSize);
                _extend = extend;
                _majorDistance = extend * 0.55;
                _largeRadius = extend * 0.45;
                _mainSize = mainSize;

                _theta = 45 * Math.PI / 180;

                _half = GetHalf(_majorDistance, _largeRadius, _theta);

                _smallSize = GetSmallSize(_majorDistance, _largeRadius, _theta);

                _borderPart = _mainSize / 2 - _half;

                LargeSize = new Size(_largeRadius, _largeRadius);
                SmallSize = _smallSize;

                JigsawConfig = jigsawConfig;
                Extend = extend;
                BuildPoints();
            }

            #endregion

            #region methods

            private void BuildPoints()
            {
                var startPoint = new Point(JigsawConfig.Left == HoleType.Out ? _extend : 0, JigsawConfig.Top == HoleType.Out ? _extend : 0);
                var pA = startPoint;
                var pB = pA + new Point(_mainSize, 0);
                var pC = pB + new Point(0, _mainSize);
                var pD = pC + new Point(-_mainSize, 0);

                var pAB = (pA + pB) / 2;
                var pBC = (pB + pC) / 2;
                var pCD = (pC + pD) / 2;
                var pDA = (pD + pA) / 2;

                var ABPoints = new List<JigsawPoint>();
                var BCPoints = new List<JigsawPoint>();
                var CDPoints = new List<JigsawPoint>();
                var DAPoints = new List<JigsawPoint>();

                #region A2B

                if (JigsawConfig.Top == HoleType.Out)
                {
                    var pAB1 = pA + new Point(_borderPart, 0);
                    var pAB2 = pAB + GetUpInsectionPoint1();
                    var pAB3 = pAB + GetUpInsectionPoint2();
                    var pAB4 = pB - new Point(_borderPart, 0);

                    ABPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pA),
                    JigsawPoint.CreateArc(pAB1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pAB2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pAB3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pAB4),
                };

                }
                else if (JigsawConfig.Top == HoleType.In)
                {
                    var pAB1 = pA + new Point(_borderPart, 0);
                    var pAB2 = pAB + ReverseY(GetUpInsectionPoint1());
                    var pAB3 = pAB + ReverseY(GetUpInsectionPoint2());
                    var pAB4 = pB - new Point(_borderPart, 0);

                    ABPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pA),
                    JigsawPoint.CreateArc(pAB1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pAB2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pAB3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pAB4),
                };

                }
                else if (JigsawConfig.Top == HoleType.Flat)
                {
                    ABPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pA),
                };
                }
                else
                {
                    throw new NotImplementedException();
                }

                #endregion

                #region B2C

                if (JigsawConfig.Right == HoleType.Out)
                {
                    var pBC1 = pB + new Point(0, _borderPart);
                    var pBC2 = pBC + GetRightInsectionPoint1();
                    var pBC3 = pBC + GetRightInsectionPoint2();
                    var pBC4 = pC - new Point(0, _borderPart);

                    BCPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pB),
                    JigsawPoint.CreateArc(pBC1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pBC2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pBC3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pBC4),
                };
                }
                else if (JigsawConfig.Right == HoleType.In)
                {
                    var pBC1 = pB + new Point(0, _borderPart);
                    var pBC2 = pBC + ReverseX(GetRightInsectionPoint1());
                    var pBC3 = pBC + ReverseX(GetRightInsectionPoint2());
                    var pBC4 = pC - new Point(0, _borderPart);

                    BCPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pB),
                    JigsawPoint.CreateArc(pBC1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pBC2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pBC3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pBC4),
                };
                }
                else if (JigsawConfig.Right == HoleType.Flat)
                {
                    BCPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pB),
                };
                }
                else
                {
                    throw new NotImplementedException();
                }

                #endregion


                #region C2D

                if (JigsawConfig.Bottom == HoleType.Out)
                {
                    var pCD1 = pC - new Point(_borderPart, 0);
                    var pCD2 = pCD + GetDownInsectionPoint2();
                    var pCD3 = pCD + GetDownInsectionPoint1();
                    var pCD4 = pD + new Point(_borderPart, 0);

                    CDPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pC),
                    JigsawPoint.CreateArc(pCD1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pCD2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pCD3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pCD4),
                };
                }
                else if (JigsawConfig.Bottom == HoleType.In)
                {
                    var pCD1 = pC - new Point(_borderPart, 0);
                    var pCD2 = pCD + ReverseY(GetDownInsectionPoint2());
                    var pCD3 = pCD + ReverseY(GetDownInsectionPoint1());
                    var pCD4 = pD + new Point(_borderPart, 0);

                    CDPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pC),
                    JigsawPoint.CreateArc(pCD1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pCD2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pCD3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pCD4),
                };
                }
                else if (JigsawConfig.Bottom == HoleType.Flat)
                {
                    CDPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pC),
                };

                }
                else
                {
                    throw new NotImplementedException();
                }

                #endregion

                #region D2A

                if (JigsawConfig.Left == HoleType.Out)
                {
                    var pDA1 = pD - new Point(0, _borderPart);
                    var pDA2 = pDA + GetLeftInsectionPoint2();
                    var pDA3 = pDA + GetLeftInsectionPoint1();
                    var pDA4 = pA + new Point(0, _borderPart);

                    DAPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pD),
                    JigsawPoint.CreateArc(pDA1, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateArc(pDA2, LargeSize, 0, SweepDirection.Clockwise, true),
                    JigsawPoint.CreateArc(pDA3, _smallSize, 0, SweepDirection.CounterClockwise, false),
                    JigsawPoint.CreateLine(pDA4),
                };
                }
                else if (JigsawConfig.Left == HoleType.In)
                {
                    var pDA1 = pD - new Point(0, _borderPart);
                    var pDA2 = pDA + ReverseX(GetLeftInsectionPoint2());
                    var pDA3 = pDA + ReverseX(GetLeftInsectionPoint1());
                    var pDA4 = pA + new Point(0, _borderPart);

                    DAPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pD),
                    JigsawPoint.CreateArc(pDA1, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateArc(pDA2, LargeSize, 0, SweepDirection.CounterClockwise, true),
                    JigsawPoint.CreateArc(pDA3, _smallSize, 0, SweepDirection.Clockwise, false),
                    JigsawPoint.CreateLine(pDA4),
                };
                }
                else if (JigsawConfig.Left == HoleType.Flat)
                {
                    DAPoints = new List<JigsawPoint>()
                {
                    JigsawPoint.CreateLine(pD),
                };
                }
                else
                {
                    throw new NotImplementedException();
                }

                #endregion

                StartPoint = startPoint;

                JigsawPoints = new List<JigsawPoint>();
                JigsawPoints.AddRange(ABPoints);
                JigsawPoints.AddRange(BCPoints);
                JigsawPoints.AddRange(CDPoints);
                JigsawPoints.AddRange(DAPoints);

            }

            #endregion

            #region utils

            private static Point ReverseX(Point p)
            {
                return new Point(-p.X, p.Y);
            }
            private static Point ReverseY(Point p)
            {
                return new Point(p.X, -p.Y);
            }

            private double GetSmallRadius(double majorDistance, double largeRadius, double angle)
            {
                var sin = Math.Sin(angle);
                var cos = Math.Cos(angle);

                var smallRadius = (majorDistance - largeRadius * cos) / (1 + cos);
                return smallRadius;
            }

            private Size GetSmallSize(double majorDistance, double largeRadius, double angle)
            {
                var smallRadius = GetSmallRadius(majorDistance, largeRadius, angle);

                return new Size(smallRadius, smallRadius);
            }

            private double GetHalf(double majorDistance, double largeRadius, double angle)
            {
                var sin = Math.Sin(angle);
                var cos = Math.Cos(angle);
                var smallRadius = GetSmallRadius(majorDistance, largeRadius, angle);
                var x = smallRadius;
                var y = (smallRadius + largeRadius) * sin;
                return y;
            }
            #endregion

            #region utils

            private Point GetRightInsectionPoint1()
            {
                var sin = Math.Sin(_theta);
                var cos = Math.Cos(_theta);
                var xf = _majorDistance - cos * _largeRadius;
                var y = _largeRadius * sin;
                return new Point(xf, -y);
            }

            private Point GetRightInsectionPoint2()
            {
                var sin = Math.Sin(_theta);
                var cos = Math.Cos(_theta);
                var xf = _majorDistance - cos * _largeRadius;
                var y = _largeRadius * sin;
                return new Point(xf, y);
            }

            private Point GetLeftInsectionPoint1()
            {
                var sin = Math.Sin(_theta);
                var cos = Math.Cos(_theta);
                var xf = _majorDistance - cos * _largeRadius;
                var y = _largeRadius * sin;
                return new Point(-xf, -y);
            }

            private Point GetLeftInsectionPoint2()
            {
                var sin = Math.Sin(_theta);
                var cos = Math.Cos(_theta);
                var xf = _majorDistance - cos * _largeRadius;
                var y = _largeRadius * sin;
                return new Point(-xf, y);
            }

            private Point GetUpInsectionPoint1()
            {
                var sin = Math.Sin(_theta);
                var cos = Math.Cos(_theta);
                var xf = _majorDistance - cos * _largeRadius;
                var y = _largeRadius * sin;
                return new Point(-y, -xf);
            }

            private Point GetUpInsectionPoint2()
            {
                var sin = Math.Sin(_theta);
                var cos = Math.Cos(_theta);
                var xf = _majorDistance - cos * _largeRadius;
                var y = _largeRadius * sin;
                return new Point(y, -xf);
            }

            private Point GetDownInsectionPoint1()
            {
                var sin = Math.Sin(_theta);
                var cos = Math.Cos(_theta);
                var xf = _majorDistance - cos * _largeRadius;
                var y = _largeRadius * sin;
                return new Point(-y, xf);
            }

            private Point GetDownInsectionPoint2()
            {
                var sin = Math.Sin(_theta);
                var cos = Math.Cos(_theta);
                var xf = _majorDistance - cos * _largeRadius;
                var y = _largeRadius * sin;
                return new Point(y, xf);
            }

            private Size GetSmallSize()
            {
                var smallRadius = GetSmallRadius(_majorDistance, _largeRadius, _theta);

                return new Size(smallRadius * 2, smallRadius * 2);
            }

            private double GetHalf()
            {
                var sin = Math.Sin(_theta);
                var cos = Math.Cos(_theta);
                var smallRadius = GetSmallRadius(_majorDistance, _largeRadius, _theta);
                var x = smallRadius;
                var y = (smallRadius + _largeRadius) * sin;
                return y;
            }
            #endregion
        }
    }

    public partial class JigsawGeometry
    {
        ///<summary>从包含延伸的全尺寸计算单个延伸尺寸</summary>
        public static double GetOutterFromFullSize(double fullSize) => FullConfigJigsawShapeData.GetOutterFromFullSize(fullSize);

        ///<summary>计算</summary>
        public static double GetFullSize(double mainSize) => FullConfigJigsawShapeData.GetFullSize(mainSize);

        ///<summary>从拼图体尺寸计算延伸的尺寸</summary>
        public static double GetOutterFromMainSize(double mainSize) => FullConfigJigsawShapeData.GetOutterFromMainSize(mainSize);
    }
}

四、补充

第三节是核心代码,如果你只抄了这里是会有问题的,因为这里是补充的内容!

在这里另外贴了一个封装了 JigsawGeometry 的 Path,旨在更方便的创建拼图块。

   public class JigsawShape : Path
   {
       #region props

       /// <summary>
       /// 主中心。
       /// </summary>
       public Point MainCenter => _jigsawGeometry.MainCenter;

       /// <summary>
       /// 主尺寸。
       /// </summary>
       public double MainSize => _jigsawGeometry.MainSize;

       /// <summary>
       /// 全尺寸。
       /// </summary>
       public Size FullSize => _jigsawGeometry.FullSize;

       /// <summary>
       /// 主体矩阵。
       /// </summary>
       public Rect MainRect => _jigsawGeometry.MainRect;

       /// <summary>
       /// 图形的配置信息。
       /// </summary>
       public JigsawConfig JigsawConfig => _jigsawGeometry.JigsawConfig;

       /// <summary>
       /// 连接处的尺寸。
       /// </summary>
       public double Outter => _jigsawGeometry.Outter;

       #endregion

       #region fields

       private JigsawGeometry _jigsawGeometry;

       #endregion

       #region ctors     

       /// <summary>
       /// 拼图图形的构造函数。
       /// </summary>
       /// <param name="mainSize">拼图块主尺寸,不包括连接处的外围尺寸。</param>
       /// <param name="jigsawConfig">该拼图块的外观。</param>
       public JigsawShape(double mainSize, JigsawConfig jigsawConfig)
       {
           _jigsawGeometry = new JigsawGeometry(mainSize, jigsawConfig);
           Data = _jigsawGeometry;
       }

       #endregion
   }
posted @ 2025-04-15 11:37  fanbal  阅读(78)  评论(0)    收藏  举报