此文主要描述我实现碎片化的便捷过程。
步骤1:
选取参考图如下(百度图库搜的):
步骤2:
根据效果图或者模型构建贝塞尔曲线,为了方便查看效果,可以设置控制点,Console.WriteLine或直接UI上显示的方式记录最终坐标,效果图如:
上图中的红线,为两条贝塞尔曲线,左侧的位置我已经通过控制点的方式调好。 上图Gif为我调整上方第二条贝塞尔的示意。
步骤3:
当上方右侧的第二条贝塞尔曲线也调整好后,就已经相当于调整好了所有的四条边。
具体原理为:最上方的一条线绕着最右边的顶点逆时针旋转90度即可得到右侧的线。右侧的线以最下方的点逆时针旋转90度就是下方的线。左侧的线也通过旋转下方的线得到。唯一需要做的就是抽一个某个点围绕中心点逆时针旋转90度后得到新点的函数。
<Canvas Width="448" Height="448" HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="160" x:Name="CvMainZm" Background="Transparent">
<Path Stroke="Red" StrokeThickness="1" Fill="Blue" x:Name="Path1Zm">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="96, 96">
<PathFigure.Segments>
<PathSegmentCollection>
<BezierSegment x:Name="Bezier1Zm" Point1="286 62" Point2="96 176" Point3="224 192"/>
<BezierSegment x:Name="Bezier2Zm" Point1="352 176" Point2="162 62" Point3="352 96"/>
<BezierSegment x:Name="Bezier3Zm" Point3="448 224"/>
<BezierSegment x:Name="Bezier4Zm" Point3="352 352"/>
<BezierSegment x:Name="Bezier5Zm" Point3="224 266"/>
<BezierSegment x:Name="Bezier6Zm" Point3="96 352"/>
<BezierSegment x:Name="Bezier7Zm" Point3="0 224"/>
<BezierSegment x:Name="Bezier8Zm" Point3="96 96"/>
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
如我的示例代码,我通过步骤1得到了 上方线的两条Bezier线的值,直接写入xaml中。(注:我提前根据步骤一的示例图,给出了每条Bezier的终点,即Point3)
<BezierSegment x:Name="Bezier1Zm" Point1="286 62" Point2="96 176" Point3="224 192"/>
<BezierSegment x:Name="Bezier2Zm" Point1="352 176" Point2="162 62" Point3="352 96"/>
旋转设置右侧线操作:
private void SetRightFirstBezier()
{
Point oPoint1 = this.Bezier2Zm.Point1;
Point oPoint2 = this.Bezier2Zm.Point2;
Point oOrigin = this.Bezier2Zm.Point3;
Point oNew = this.CarouselPointAnticlockwise(oPoint1, oOrigin);
Point oNew2 = this.CarouselPointAnticlockwise(oPoint2, oOrigin);
this.Bezier3Zm.Point1 = oNew2;
this.Bezier3Zm.Point2 = oNew;
}
private void SetRightSecondBezier()
{
Point oPoint1 = this.Bezier1Zm.Point1;
Point oPoint2 = this.Bezier1Zm.Point2;
Point oOrigin = this.Bezier2Zm.Point3;
Point oNew = this.CarouselPointAnticlockwise(oPoint1, oOrigin);
Point oNew2 = this.CarouselPointAnticlockwise(oPoint2, oOrigin);
this.Bezier4Zm.Point1 = oNew2;
this.Bezier4Zm.Point2 = oNew;
}
// 绕顶点旋转得到新点坐标
private Point CarouselPointAnticlockwise(Point oPoint, Point oOrigin)
{
double dY = oOrigin.Y - oPoint.Y;
double dX = oOrigin.X - oPoint.X;
double dXAdd = dX - dY;
double dYAdd = dX + dY;
double dNewX = oPoint.X + dXAdd;
double dNewY = oPoint.Y + dYAdd;
return new Point(dNewX, dNewY);
}
下方和左方以此类推。 这样通过后台点旋转的旋转计算处理就可得到所有的Bezier。实现效果如下图。
步骤4:
通过步骤3得到的Path与RectangleGeometry进行组合,设置成不同碎片的Clip属性即可得到所有用到的碎片,效果图如下:
组合过程中,部分碎片的Clip可通过其他碎片的Clip直接翻转或者顺时针、逆时针旋转直接得到。如:
<CombinedGeometry x:Key="KeyPieceType4"
Geometry1="{StaticResource KeyPieceType1}">
<CombinedGeometry.Transform>
<TransformGroup>
<ScaleTransform ScaleX="-1" CenterX="224" ScaleY="-1" CenterY="224" />
<RotateTransform Angle="-90" CenterX="224" CenterY="224"/>
</TransformGroup>
</CombinedGeometry.Transform>
</CombinedGeometry>
步骤5:
可以通过提前设定矩阵碎片样式的固定模式得到所有切图,也可以随机生成每个碎片,每个片的形状通过检索左侧右边是否凹凸和上侧碎片下边是否凹凸获得。我的碎片化效果如下图。