.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);
最后,運行效果如下:
總結
前言中關於“科學”的論述,只是開個玩笑……但繪制這個太極圖形,還是需要一些技巧的。
本文中的所有可直接運行的代碼,已經上傳到我的Github
:https://github.com/sdcb/blog-data/tree/master/2020/20200119-draw-taiji-using-dotnet
喜歡的朋友請關注我的微信公眾號:【DotNet騷操作】