.NET绘制旋转太极图

.NET绘制旋转太极图

我之前发了一篇《用.NET写“算命”程序》的文章,但有人纷纷提出了质疑,认为没有“科学”(mi xin)依据😂。

所谓“太极生两仪,两仪生四象,四象生八卦,八卦定吉凶,吉凶生大业”,因此,我只要证明.NET可以将太极图绘制出来,不就说明.NET算命的“科学”是有依据了嘛😂

首先来看一下最终效果:

太极的构成

仔细观察这个太极图,它分为以下几部分:

  • 基本窗口
  • 白色左圆、黑色右圆
  • 白色左圆中的黑色1/4圆,黑色右圆中的白色1/4
  • 黑色、白色半圆
  • 旋转动画

因此制作时,我们从这些方面着手制作。

基本窗口

首先需要一个渲染窗口,使用FlysEngine.Desktop,可以轻松制作一个:

using var taiji = new RenderWindow();
taiji.Draw += (o, e) =>
{
    e.Clear(Color.CornflowerBlue);
};
RenderLoop.Run(taiji, () => taiji.Render(1, 0));

运行效果如下:

白色左圆、黑色右圆

Direct2D有绘制圆和非常简单的API,可以直接调用,但在此之前需要先确定该圆的半径,我们最窗口的较小值减去5单位的边距(Margin),顺便计算一下中心点,以便于稍后做矩阵变换:

float GetMinEdge() => Math.Min(
    taiji.XResource.RenderTarget.Size.Width, 
    taiji.XResource.RenderTarget.Size.Height);

Vector2 GetCenter() => new Vector2(
    taiji.XResource.RenderTarget.Size.Width / 2, 
    taiji.XResource.RenderTarget.Size.Height / 2);

float GetR() => (GetMinEdge() - 5.0f) / 2;

顺便定义一下黑、白两种颜色:

Color Color_Black = Color.Black;
Color Color_White = Color.White;

所以绘制的代码如下:

float scale = GetR();
Vector2 center = GetCenter();
Matrix3x2 rotation = Matrix3x2.Rotation(angle);

ctx.Transform = rotation * Matrix3x2.Scaling(scale, scale) * Matrix3x2.Translation(center);
ctx.FillEllipse(new Ellipse(new Vector2(0.5f, 0), 0.5f, 0.5f), o.XResource.GetColor(Color_Black));
ctx.FillEllipse(new Ellipse(new Vector2(-0.5f, 0), 0.5f, 0.5f), o.XResource.GetColor(Color_White));

此时,运行效果如下:

1/4圆

1/4圆是太极的精髓之一,正是它代表了阴与阳的互生互补。

其本质就是圆的中心还有另一个颜色的圆,代码相对简单,只需在上文代码中添加如下即可:

ctx.FillEllipse(new Ellipse(new Vector2(0.5f, 0), 0.25f, 0.25f), o.XResource.GetColor(Color_White));
ctx.FillEllipse(new Ellipse(new Vector2(-0.5f, 0), 0.25f, 0.25f), o.XResource.GetColor(Color_Black));

此时,运行效果如下:

黑白半圆

还需要最后临门一脚,就是需要一个更大的圆,包含这个图形。仔细想想,其实这个“更大的圆”是两个不同颜色的半圆,在Direct2D中,需要使用Geometry来实现,首先来定义这个半圆:

using var arc = new PathGeometry(taiji.XResource.Direct2DFactory);
var sink = arc.Open();
sink.BeginFigure(new Vector2(-1f, 0), FigureBegin.Filled);
sink.AddArc(new ArcSegment
{
	Point = new Vector2(1f, 0), 
	Size = new Size2F(1f, 1f), 
	RotationAngle = 0.0f, 
	SweepDirection = SweepDirection.Clockwise, 
	ArcSize = ArcSize.Large, 
});
sink.EndFigure(FigureEnd.Open);
sink.Close();

然后在Draw事件的Clear之后,首先绘制黑色上半圆:

ctx.Transform = Matrix3x2.Scaling(scale, scale) * Matrix3x2.Translation(center);
ctx.FillGeometry(arc, o.XResource.GetColor(Color_Black));

运行效果如下:

然后再绘制白色下半圆,注意代码也要写在绘制左右圆的代码之前:

ctx.Transform = Matrix3x2.Scaling(scale, scale) * Matrix3x2.Rotation((float)Math.PI) * Matrix3x2.Translation(center);
ctx.FillGeometry(arc, o.XResource.GetColor(Color_White));

运行效果如下:

此时就已经是一个完整的太极图标了,最后还需要添加旋转动画。

旋转动画

动画的本质,是图形的坐标、旋转随着时间的移动,而有规律的移动。这里特指旋转,我们定义每秒旋转0.25圈(即每4秒转一圈):

const float Speed = 0.25f;

转换为旋转角,需要在UpdateLogic中添加代码如下:

float angle = 0.0f;
taiji.UpdateLogic += (w, dt) =>
{
	angle += MathUtil.Mod2PI(Speed * MathUtil.TwoPi * dt);
};

最后需要将旋转角angle转换为矩阵变换,注意在操作矩阵乘法时,旋转往往要放在第一位,否则旋转中心点可能会出现意外,代码如下,用于替换上文中的直接绘制代码:

Matrix3x2 rotation = Matrix3x2.Rotation(angle);
ctx.Transform = rotation * Matrix3x2.Scaling(scale, scale) * Matrix3x2.Translation(center);
ctx.FillGeometry(arc, o.XResource.GetColor(Color_Black));

ctx.Transform = rotation * Matrix3x2.Scaling(scale, scale) * Matrix3x2.Rotation((float)Math.PI) * Matrix3x2.Translation(center);
ctx.FillGeometry(arc, o.XResource.GetColor(Color_White));

ctx.Transform = rotation * Matrix3x2.Scaling(scale, scale) * Matrix3x2.Translation(center);

最后,运行效果如下:

总结

前言中关于“科学”的论述,只是开个玩笑……但绘制这个太极图形,还是需要一些技巧的。

本文中的所有可直接运行的代码,已经上传到我的Githubhttps://github.com/sdcb/blog-data/tree/master/2020/20200119-draw-taiji-using-dotnet

喜欢的朋友请关注我的微信公众号:【DotNet骚操作】

DotNet骚操作

posted @ 2020-01-19 15:32  .NET骚操作  阅读(...)  评论(...编辑  收藏