こんにちは、きうちです!
久々に投稿します^^
今回はクリスマスということで、プログラムでクリスマスリースを描いてみたいと思います。
使用するのは例によってC#です。Windowsフォームアプリケーションとして作成します。
★ ★ ★
この季節になるとそこかしこで見かけますよね、クリスマスリース。
あれって円形をしているし、モールっぽいものを円状に並べればいけるんじゃね?と思っていました。
長らく思っていましたが、ついにプログラムにしてみました。
まずは基本の、円。
三角関数を使って描きます。
フォームを512×512にして、FormBorderStyleをFixedToolWindowにして、背景を黒にして・・・
描画はフォームのPaintイベントで。※以下は描画部分の抜粋です。
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.Black);
DrawWreath(e.Graphics);
}
private void DrawWreath(Graphics g)
{
double xx = -1, yy = -1, r = 30;
for (int i = 0; i <= 360; i += 5) {
double theta = i * Math.PI / 180;
double x = Math.Cos(theta) * 200 + 245;
double y = Math.Sin(theta) * 200 + 240;
if (xx > 0)
{
g.DrawLine(Pens.AliceBlue, (float)xx, (float)yy, (float)x, (float)y);
}
xx = x;
yy = y;
DrawCircle(g, (float)x, (float)y, (float)r);
}
}
private void DrawCircle(Graphics g, float x, float y, float r)
{
g.DrawEllipse(Pens.AliceBlue, x - r, y - r, r * 2, r * 2);
}
そうするとこうなります。

そしたら、今度は各箇所に書いている円をモールっぽいものにします。
DrawCircleメソッドをそのように改造します。
やり方としては、各々の中心点から放射状にランダムに緑と白の線を伸ばします。
やってみたら白と緑の数を均等にするとバランスが悪かったので、緑2:白1にします。
また、白は二重線、緑は黒い線を隣に描きます。
private void DrawCircle(Graphics g, float x, float y, float r)
{
for (int j = 0; j < 50; j++)
{
double theta2 = random.NextDouble() * Math.PI * 2;
double x1 = x + Math.Cos(theta2) * r;
double y1 = y + Math.Sin(theta2) * r;
Pen color = new Pen[] { Pens.Green, Pens.Green, Pens.White }[j % 3];
Pen color2 = new Pen[] { Pens.Black, Pens.Black, Pens.White }[j % 3];
g.DrawLine(color, (float)x, (float)y, (float)x1, (float)y1);
g.DrawLine(color2, (float)x + 1, (float)y, (float)x1 + 1, (float)y1);
}
}
1個だけ描かせると

こんな感じ。
円形に並べると・・・

こう!
ほら、だいぶクリスマスリースっぽくなった^^
★ ★ ★
こうなると、なんかもう少し飾りが欲しくなりますね。
リボン付けましょうか。
というわけで、ちょっとこしらえてきました。
Inkscapeを使ってSVGを作りました。

SVGファイルを開いて、Pathのデータだけ持ってきて、ちょっとSVGパーサ(時間がなかったので手抜きですが)を書いて・・・
private string data = "M 18.898809,103.1875 9.8273807,90.525294 20.410715,76.351189 52.916666,70.303572 83.910712,89.958331 122.84226,69.925593 l 27.97024,5.669645 6.80357,13.040178 -6.04762,13.040174 -25.3244,6.4256 -35.15179,-5.66964 27.97024,24.19047 13.60714,24.19048 13.22917,53.67262 -12.09524,-6.80357 -9.82738,13.22916 -10.58333,-48.38095 -11.33929,-38.93154 -18.520832,-18.14286 -13.229168,15.49702 -9.827381,41.1994 -1.133929,49.13691 -11.339285,-11.33929 -14.363095,4.91369 9.071428,-54.05059 8.315478,-21.54464 22.300593,-27.21429 -32.127976,7.18155 z";
private List list = new List();
private void MakeRibbon()
{
string[] ds = data.Split(' ');
int mode = 0;
float x1 = 0, y1 = 0, x2 = 0, y2 = 0;
float xmax = 0, ymax = 0, xmin = 999, ymin = 999;
for (int i = 0; i < ds.Length; i++) {
if (ds[i] == "M")
{
mode = 1;
}
else if (ds[i] == "l")
{
mode = 2;
}
else if (ds[i].IndexOf(",") > -1)
{
string[] xy = ds[i].Split(',');
float x = float.Parse(xy[0]) * 2F;
float y = float.Parse(xy[1]) * 1.5F;
if (mode == 1)
{
x2 = x;
y2 = y;
}
else if (mode == 2)
{
x2 = x1 + x;
y2 = y1 + y;
}
x1 = x2;
y1 = y2;
list.Add(new PointF(x1 + 80, y1 - 80));
if (xmin > x1 + 80) xmin = x1 + 80;
if (xmax < x1 + 80) xmax = x1 + 80;
if (ymin > y1 - 80) ymin = y1 - 80;
if (ymax < y1 - 80) ymax = y1 - 80;
}
}
list.Add(new PointF(list[0].X, list[0].Y));
float w = xmax - xmin;
float h = ymax - ymin;
float halfW = w / 2F;
float halfH = h / 2F;
for (int i = 0; i < list.Count; i++)
{
list[i] = new PointF((list[i].X - halfW) / halfW * 200 + 110, (list[i].Y - halfH) / halfH * 100 + 80);
}
}
これをコンストラクタから呼んでおきます。
public Form1()
{
InitializeComponent();
MakeRibbon();
}
それで、Paintイベントメソッドから、輪っかを描いた後にリボンを描くメソッドを呼び出します。
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.Black);
DrawWreath(e.Graphics);
DrawRibbon(e.Graphics);
}
private void DrawRibbon(Graphics g)
{
g.FillPolygon(Brushes.Red, list.ToArray());
}
するとこうなります。

うーん・・・なんか、取って付けた感満載w
ちょっとサイズと色を変えながら重ねますか。
で、真ん中に円を描いて。
private Listlist2 = new List (); private List list3 = new List (); private void MakeRibbon() { ... for (int i = 0; i < list.Count; i++) { list2.Add(new PointF((list[i].X - halfW) / halfW * 190 + 115, (list[i].Y - halfH) / halfH * 80 + 70)); list3.Add(new PointF((list[i].X - halfW) / halfW * 180 + 120, (list[i].Y - halfH) / halfH * 60 + 60)); list[i] = new PointF((list[i].X - halfW) / halfW * 200 + 110, (list[i].Y - halfH) / halfH * 100 + 80); } } private void DrawRibbon(Graphics g) { g.FillPolygon(Brushes.Red, list.ToArray()); g.FillPolygon(Brushes.White, list2.ToArray()); g.FillPolygon(Brushes.Red, list3.ToArray()); g.FillEllipse(Brushes.Red, 220, 10, 60, 80); g.FillEllipse(Brushes.White, 225, 15, 50, 70); g.FillEllipse(Brushes.Red, 230, 20, 40, 60); }
そうすると。

・・・まだなんかごまかし切れていない気がしますが、まあよしとしましょう。
じゃあ、あとは字を入れてみようかな。
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.Black);
DrawWreath(e.Graphics);
DrawRibbon(e.Graphics);
DrawString(e.Graphics);
}
private Font font = new Font("Comic Sans MS", 64);
private void DrawString(Graphics g)
{
g.DrawString("Merry", font, Brushes.Black, 104, 204);
g.DrawString("Merry", font, Brushes.Yellow, 100, 200);
g.DrawString("Christmas!", font, Brushes.Black, 24, 304);
g.DrawString("Christmas!", font, Brushes.Yellow, 20, 300);
}

うん、いいんじゃないですかね♪
本当は字を光らせたりBGM流したりしたいけど・・・それはまた機会があれば!・・・って来年か!?w
ではまた!
P.S. 完成したソースはまた後でGitHubに載せておきます^^
★ ★ ★
2024/12/24夜 追記
ソースをGitHubに載せました。こちら→ここをクリック
リボンの位置がもう少し左だなーと思ったので、少し左に寄せてあります。
VisualStudio2022で作成しています。
あと本文の方ですが、最初のリボンを描くメソッドが抜けていたので追加しました。