3. 図形を描く¶
この章では、色や図形を表現するクラスを学び、それらを使って画面に図形を描きます。
Siv3D では、2D 図形に関して以下のようなクラスがあります。
2D 図形に関する主なクラス
型名 | 説明 |
---|---|
Point | 2 次元のベクトル(要素は int32 ) |
Vec2 | 2 次元のベクトル(要素は double ) |
Size | 横、縦の大きさ(要素は int32 )(Point の別名) |
SizeF | 横、縦の大きさ(要素は double )(Vec2 の別名) |
Line | 線分 |
Circle | 円 |
Ellipse | 楕円 |
Rect | 長方形(要素は int32 ) |
RectF | 長方形(要素は double ) |
Triangle | 三角形 |
Quad | 凸四角形 |
RoundRect | 角丸長方形 |
Polygon | 多角形(穴も持てる) |
MultiPolygon | 多角形の集合(Array<Polygon> の置き換え) |
Bezier2 | 二次ベジェ曲線 |
Bezier3 | 三次ベジェ曲線 |
LineString | 連続する線分(Array<Vec2> の置き換え) |
Spline2D | スプライン曲線 |
Circular | 円座標 |
OffsetCircular | オフセット付き円座標 |
Shape2D | 多角形作成ユーティリティ |
3.1 円を描く¶
Siv3D では、図形クラスのオブジェクトを作成し、その draw()
メンバ関数を呼んで描画を行います。円を描くときは Circle
を作成し、その .draw()
を呼びます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 中心座標 (400, 300), 半径 20 の円を描く
Circle{ 400, 300, 20 }.draw();
}
}
3.2 円の大きさを変える¶
Circle{}
の最後に指定するパラメータは円の半径です。この値を大きくすれば、描画される円も大きくなります。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 中心座標 (400, 300), 半径 100 の円を描く
Circle{ 400, 300, 100 }.draw();
}
}
3.3 X 座標がマウスカーソルと連動する円を描く¶
円がマウスカーソルの座標に連動して動くようにしてみましょう。Circle{}
の最初に指定するパラメータは円の中心の X 座標です。この値をマウスカーソルの X 座標にしてみます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 中心座標 (マウスの X 座標, 300), 半径 100 の円を描く
Circle{ Cursor::Pos().x, 300, 100 }.draw();
}
}
前章で、Print
を使って表示したメッセージはいつまでも画面に残りましたが、それは例外的なルールです。Print
以外のすべての描画は System::Update()
のたびに背景の色でリセットされます。
3.4 マウスカーソルと連動する円を描く¶
Siv3D で X 座標、Y 座標 2 つの値を受け取る関数は、多くの場合、1 つの Point
型、もしくは Vec2
型を受け取る関数オーバーロードを提供しています。
int32 x = 10, y = 20;
Circle{ x, y, 10 };
Point p1{ 10, 20 };
Circle{ p1, 10 };
Vec2 p2{ 10.0, 20.0 };
Circle{ p2, 10 };
Circle
も、「X 座標」「Y 座標」「半径」の 3 つの引数からではなく、「中心座標」「半径」の 2 つの引数から構築するオーバーロードがあります。これを使って円をマウスカーソルと連動させます。
C++ の文法復習「関数のオーバーロード」
引数の型や個数が異なる関数を同じ名前で定義できるという C++ の言語機能です。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 中心座標がマウスの座標, 半径 100 の円を描く
Circle{ Cursor::Pos(), 100 }.draw();
}
}
次のような書き方もできますが、前述のコードのほうが便利でしょう。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 中心座標 (マウスの X 座標, マウスの Y 座標), 半径 100 の円を描く
Circle{ Cursor::Pos().x, Cursor::Pos().y, 100 }.draw();
}
}
3.5 色を付ける¶
図形に色を付けたいときは .draw()
関数に色を渡します。
色の指定の方法は以下の 4 つをよく使います。
色の表現 | 値の範囲 |
---|---|
Palette::色名 | Web カラー の名前で色を指定 |
ColorF{ r, g, b, a } | 0.0 - 1.0 の範囲で RGBA の各成分を指定 |
Color{ r, g, b, a } | 0 - 255 の整数の範囲で RGBA の各成分を指定 |
HSV{ h, s, v, a } | 色相 h , 彩度 s , 明度 v とアルファ値 a の各成分を指定。h は 0.0 - 360.0 (370.0 は 10.0 と同じ). s, v, a は 0.0 - 1.0,の範囲 |
Palette::色名 は、Palette::Orange
, Palette::Yellow
のように、RGB 値がわからなくても使えます。
ColorF は、Siv3D で最も使われる色の表現形式です。
Color は、Image
型の要素で使われます。Siv3D で画像処理をするときに使われる形式です。
HSV は、赤っぽい、青っぽいなど色の種類を表す 色相 (hue) と、色の鮮やかを表す 彩度 (saturation), 色の明るさを表す 明度 (value) を使った HSV 色空間で色を表現します。
ColorF
, Color
, HSV
はいずれも アルファ値 a
を持ちます。アルファ値は「不透明度」を表し、最大値 (ColorF
と HSV
の場合 1.0, Color
の場合 255) ではまったく透過しませんが、値を小さくするとそれに応じて背景の透過が増し、0 になると完全に透明になります。
色の指定はプログラムで何度も登場するため、次のような短い書き方も用意されています。例えば ColorF{ 0.5 }
は ColorF{ 0.5, 0.5, 0.5, 1.0 }
と同等です。
短い書き方 | 意味 |
---|---|
ColorF{ r, g, b } |
ColorF{ r, g, b, 1.0 } |
ColorF{ rgb, a } |
ColorF{ rgb, rgb, rgb, a } |
ColorF{ rgb } |
ColorF{ rgb, rgb, rgb, 1.0 } |
Color{ r, g, b } |
Color{ r, g, b, 255 } |
Color{ rgb, a } |
Color{ rgb, rgb, rgb, a } |
Color{ rgb } |
Color{ rgb, rgb, rgb, 255 } |
HSV{ h, s, v } |
HSV{ h, s, v, 1.0 } |
HSV{ h, a } |
HSV{ h, 1.0, 1.0, a } |
HSV{ h } |
HSV{ h, 1.0, 1.0, 1.0 } |
色の付いたいくつかの円を描いてみましょう。.draw()
に色を指定しなかった場合のデフォルトの色は Palette::White
(ColorF{ 1.0, 1.0, 1.0, 1.0 }
) です。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 左から順に 7 つの円を描く
Circle{ 100, 200, 40 }.draw();
Circle{ 200, 200, 40 }.draw(Palette::Green);
Circle{ 300, 200, 40 }.draw(Palette::Skyblue);
Circle{ 400, 200, 40 }.draw(ColorF{ 1.0, 0.8, 0.0 });
Circle{ 500, 200, 40 }.draw(Color{ 255, 127, 127 });
Circle{ 600, 200, 40 }.draw(HSV{ 160.0, 1.0, 1.0 });
Circle{ 700, 200, 40 }.draw(HSV{ 160.0, 0.75, 1.0 });
// 半透明の円
Circle{ Cursor::Pos(), 80 }.draw(ColorF{ 0.0, 0.5, 1.0, 0.8 });
}
}
3.6 背景の色を変える¶
シーンの背景色を変えるには Scene::SetBackground()
に色を渡します。新しい背景色は、それ以降の System::Update()
で画面の描画内容をリセットするときから反映されます。背景色は、一度設定すると再度変更されるまで同じ設定が使われます。
# include <Siv3D.hpp>
void Main()
{
// 背景色を ColorF{ 0.3, 0.6, 1.0 } に設定
Scene::SetBackground(ColorF{ 0.3, 0.6, 1.0 });
while (System::Update())
{
Circle{ Cursor::Pos(), 80 }.draw();
}
}
3.7 背景の色を時間の経過とともに変える¶
Scene::Time()
は プログラムの経過時間(秒) を double
型の値で返します。これを用いて、時間に応じて背景色の色相を変化させてみましょう。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 色相 hue = (経過時間 (秒) * 60)
const double hue = (Scene::Time() * 60);
Scene::SetBackground(HSV{ hue, 0.6, 1.0 });
}
}
3.8 長方形を描く¶
長方形を描くときは Rect
を作成して .draw()
します。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 座標 (20, 40) を左上の基準位置にして、幅 400, 高さ 100 の長方形を描く
Rect{ 20, 40, 400, 100 }.draw();
// 座標 (100, 200) を左上の基準位置にして、幅が 80 の正方形を描く
Rect{ 100, 200, 80 }.draw(Palette::Orange);
// 座標 (400, 300) を中心の基準位置にして、幅 80, 高さ 40 の長方形を描く
Rect{ Arg::center(400, 300), 80, 40 }.draw(Palette::Pink);
// マウスカーソルの座標を中心の基準位置にして、幅が 100 の正方形を描く
Rect{ Arg::center(Cursor::Pos()), 100 }.draw(ColorF{ 1.0, 0.0, 0.0, 0.5 });
// 座標や大きさを浮動小数点数 (小数を含む数)で指定したい場合は RectF
RectF{ 200.4, 450.3, 390.5, 122.5 }.draw(Palette::Skyblue);
}
}
図形は draw()
した順番に描画されます。このプログラムの、マウスカーソルに追従する赤い正方形と画面の下にある水色の大きな長方形を比べると、後者のほうが上に描画されます。
Rect
型は左上の座標と幅、高さをそれぞれ int32 x
, int32 y
, int32 w
, int32 h
というメンバ変数で表します。整数ではなく浮動小数点数で扱いたい場合は、すべての要素が double
型である RectF
を使います。
3.9 枠を描く¶
図形の枠だけを描きたい場合、.draw()
の代わりに .drawFrame()
を使います。.drawFrame()
の第 1 引数には図形の内側方向への太さを、第 2 引数には外側方向への太さを指定します。図形の .draw()
や .drawFrame()
の戻り値はその図形自身なので、rect.draw().drawFrame()
のように関数を続けて書くこともできます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 長方形の内側に 3px の枠を描く
Rect{ 100, 100, 100, 30 }
.drawFrame(3, 0);
// 長方形の外側に 3px の枠を描く
Rect{ 220, 100, 100, 30 }
.drawFrame(0, 3);
// 長方形と、その内側 3px と外側 3px に枠を描く
Rect{ 200, 200, 400, 100 }
.draw(Palette::White)
.drawFrame(3, 3, Palette::Orange);
// 円の内側 1px と外側 1px に枠を描く
Circle{ Cursor::Pos(), 40 }
.drawFrame(1, 1, Palette::Seagreen);
}
}
3.10 線分を描く¶
始点と終点を指定して線分を描くときは Line
を作成して .draw()
します。
.draw()
のパラメータには描画する線分の太さと色を指定します。
線分の両端を丸くしたり、点線にしたりするなど、スタイルの変更もできます。
スタイル | 意味 |
---|---|
LineStyle::SquareCap | 両端が四角い線(デフォルト) |
LineStyle::RoundCap | 両端が丸い線 |
LineStyle::Uncapped | 両端がはみ出ない線 |
LineStyle::SquareDot | 四角いドットの線 |
LineStyle::RoundDot | 丸いドットの線 |
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 座標 (100, 100) から (400, 150) まで太さ 4px の線分を描く
Line{ 100, 100, 400, 150 }.draw(4, Palette::Yellow);
// 座標 (400, 300) からマウスカーソルの座標まで太さ 10px の線分を描く
Line{ 400, 300, Cursor::Pos() }.draw(10, Palette::Skyblue);
// 通常の線
Line{ 100, 400, 700, 400 }.draw(12, Palette::Orange);
// 両端が丸い線
Line{ 100, 450, 700, 450 }.draw(LineStyle::RoundCap, 12, Palette::Orange);
// 四角いドットの線
Line{ 100, 500, 700, 500 }.draw(LineStyle::SquareDot, 12, Palette::Orange);
// 丸いドットの線
Line{ 100, 550, 700, 550 }.draw(LineStyle::RoundDot, 12, Palette::Orange);
}
}
3.11 三角形を描く¶
三角形を描くには、Triangle
を作成して .draw()
します。Triangle
は次のように作成できます。
- 3 つの頂点座標を時計回りに指定する
- 正三角形の重心座標と辺の長さ、回転角度を指定する
Siv3D における角度は、2π = 360°
のラジアンで表現します。Math::ToRadians()
関数で度数法からラジアン角へ変換できるほか、_deg
サフィックスを使うことでリテラルで記述することもできます。
X 座標と Y 座標の組は Point
型や Vec2
型で表現できます。Point
型は各成分が int32
型で、Vec2
型は double
型です。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 座標 (100, 100), (400, 300), (100, 300) で構成される三角形を描く
Triangle{ 100, 100, 400, 300, 100, 300 }.draw();
// 座標 (300, 100) を重心とする、1 辺が 80px の三角形を描く
Triangle{ 300, 100, 80 }.draw(Palette::Orange);
// 座標 (400, 100) を重心とする、1 辺が 80px の三角形を時計回りに 15° 回転させて描く
Triangle{ 400, 100, 80, 15_deg }.draw(Palette::Seagreen);
// 座標 (500, 100) を重心とする、1 辺が 80px の三角形を時計回りに 30° 回転させて描く
Triangle{ 500, 100, 80, 30_deg }.draw(Palette::Pink);
// 3 つの頂点座標を Point や Vec2 型で指定
Triangle{ Cursor::Pos(), Vec2{ 700, 500 }, Vec2{ 100, 500 } }.draw(Palette::Skyblue);
}
}
3.12 凸な四角形を描く¶
Rect
や RectF
では、各辺が X 軸、Y 軸に平行な長方形しか定義できませんでしたが、Quad
を使うと 4 つの頂点座標を時計回りに指定して四角形を定義できます。ただし、Quad
で定義される四角形は 180° 以上の内角を含まない形状(すべての角が凸)である必要があります。凹角を含む四角形を定義したい場合はのちにで出てくる Polygon
型を使います。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 4 つの頂点座標を指定して四角形を描く
Quad{ Vec2{ 100, 100 }, Vec2{ 150, 100 }, Vec2{ 300, 300 }, Vec2{ 100, 300 } }.draw();
Quad{ Vec2{ 300, 400 }, Vec2{ 500, 100 }, Vec2{ 600, 200 }, Vec2{ 500, 500 } }.draw(Palette::Skyblue);
}
}
Rect
や RectF
を作成し、.rotated()
または .rotatedAt()
を使うと、長方形を回転させて Quad
を作成できます。その Quad
を .draw()
する一連の操作を次のように 1 行で書けます。Rect::pos
は Rect
の左上の座標を Point
型で、RectF::pos
は RectF
の左上の座標を Vec2
型で表すメンバ変数です。
# include <Siv3D.hpp>
void Main()
{
constexpr Rect rect{ 150, 200, 400, 100 };
while (System::Update())
{
rect.draw();
// 時計回りに 45° 回転した長方形
rect.rotated(45_deg).draw(Palette::Orange);
// 長方形の左上の座標を回転の軸として時計回りに 60° 回転した長方形
rect.rotatedAt(rect.pos, 60_deg).draw(Palette::Skyblue);
}
}
Rect
や RectF
を作成し、.shearedX()
または .shearedY()
を使うと、長方形の辺を X 軸または Y 軸に沿ってスライドさせた平行四辺形を Quad
型として作成できます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 長方形の辺を X 軸方向に 30px ずつスライドさせた平行四辺形
Rect{ 100, 50, 200, 100 }.drawFrame(1, 0)
.shearedX(30).draw(Palette::Skyblue);
// 長方形の辺を Y 軸方向に -50px ずつスライドさせた平行四辺形
Rect{ 400, 150, 300, 200 }.drawFrame(1, 0)
.shearedY(-50).draw(Palette::Orange);
}
}
3.13 楕円を描く¶
楕円を描くときは Ellipse
を作成して .draw()
します。中心の座標と X 軸方向の半径、Y 軸方向の半径を指定して Ellipse
を作成します。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 中心 (300, 200), X 軸方向の半径 200, Y 軸方向の半径 100 の楕円
Ellipse{ 300, 200, 200, 100 }.draw(Palette::Skyblue);
// 中心 (600, 400), X 軸方向の半径 50, Y 軸方向の半径 150 の楕円
Ellipse{ 600, 400, 50, 150 }.draw(Palette::Orange);
}
}
3.14 角丸長方形を描く¶
角が丸い長方形を描くには、RoundRect
を作成して .draw()
します。RectF
と同じパラメータに加えて、最後に角の曲線の半径を指定します。Rect
や RectF
の .rounded()
メンバ関数を使って、Rect
や RectF
から RoundRect
を作成することもできます。
# include <Siv3D.hpp>
void Main()
{
constexpr Rect rect{ 100, 350, 500, 200 };
while (System::Update())
{
// RectF(100, 100, 200, 100) の角を 10px 丸めた角丸長方形
RoundRect{ 100, 100, 200, 100, 10 }.draw();
// RectF(Arg::center(400, 300), 200, 80) の角を 5px 丸めた角丸長方形
RoundRect{ Arg::center(400, 300), 200, 80, 5 }.draw(Palette::Skyblue);
// 長方形 rect の角を 40px 丸めた角丸長方形
rect.rounded(40).draw(Palette::Orange);
}
}
3.15 多角形を描く¶
複雑な多角形を簡単に作成できる関数が用意されています。これらの関数の戻り値である Shape2D
型のオブジェクトを .draw()
, .drawFrame()
することで図形を描けます。関数のうち、引数に double angle
をとるものは、時計回りの回転の角度を指定できます。
関数名 | 形状 | 引数 |
---|---|---|
Shape2D::Cross | ✖ マーク | double r, double width, const Vec2& center = Vec2{ 0, 0 }, double angle = 0.0 |
Shape2D::Plus | +マーク | double r, double width, const Vec2& center = Vec2{ 0, 0 }, double angle = 0.0 |
Shape2D::Pentagon | 正五角形 | double r, const Vec2& center = Vec2{ 0, 0 }, double angle = 0.0 |
Shape2D::Hexagon | 正六角形 | double r, const Vec2& center = Vec2{ 0, 0 }, double angle = 0.0 |
Shape2D::Ngon | 正 N 角形 | uint32 n, double r, const Vec2& center = Vec2{ 0, 0 }, double angle = 0.0 |
Shape2D::Star | 五芒星 | double r, const Vec2& center = Vec2{ 0, 0 }, double angle = 0.0 |
Shape2D::Nstar | 星 | uint32 n, double rOuter, double rInner, const Vec2& center = Vec2{ 0, 0 }, double angle = 0.0 |
Shape2D::Arrow | 矢印 | const Vec2& from, const Vec2& to, double width, const Vec2& headSize |
Shape2D::Arrow | 矢印 | const Line& line, double width, const Vec2& headSize |
Shape2D::DoubleHeadedArrow | 両方向矢印 | const Vec2& from, const Vec2& to, double width, const Vec2& headSize |
Shape2D::DoubleHeadedArrow | 両方向矢印 | const Line& line, double width, const Vec2& headSize |
Shape2D::Rhombus | ひし形 | double w, double h, const Vec2& center = Vec2{ 0, 0 }, double angle = 0.0 |
Shape2D::RectBalloon | 長方形の吹き出し | const RectF& rect, const Vec2& target, double pointingRootRatio = 0.5 |
Shape2D::Stairs | 階段形 | const Vec2& base, double w, double h, uint32 steps, bool upStairs = true |
Shape2D::Heart | ハート形 | double r, const Vec2& center = Vec2{ 0, 0 }, double angle = 0.0 |
Shape2D::Squircle | 四角と円の中間形 | double r, const Vec2& center, uint32 quality |
# include <Siv3D.hpp>
void Main()
{
// ウィンドウおよびシーンを 1000x600 にリサイズ
Window::Resize(1000, 600);
while (System::Update())
{
Shape2D::Cross(80, 10, Vec2{ 100, 100 }).draw(Palette::Skyblue);
Shape2D::Plus(80, 10, Vec2{ 300, 100 }).draw(Palette::Skyblue);
Shape2D::Pentagon(80, Vec2{ 500, 100 }).draw(Palette::Skyblue);
Shape2D::Hexagon(80, Vec2{ 700, 100 }).draw(Palette::Skyblue);
// 30° 回転させる
Shape2D::Hexagon(80, Vec2{ 900, 100 }, 30_deg).draw(Palette::Skyblue);
// 正十角形
Shape2D::Ngon(10, 80, Vec2{ 100, 300 }).draw(Palette::Skyblue);
Shape2D::Star(80, Vec2{ 300, 300 }).draw(Palette::Skyblue);
// rOuter は外周の半径、rInner は内周の半径
Shape2D::NStar(10, 80, 60, Vec2{ 500, 300 }).draw(Palette::Skyblue);
// headSize は三角形の幅と高さ
Shape2D::Arrow(Line{ 640, 340, 760, 260 }, 20, Vec2{ 40, 30 }).draw(Palette::Skyblue);
Shape2D::DoubleHeadedArrow(Line{ 840, 340, 960, 260 }, 20, Vec2{ 40, 30 }).draw(Palette::Skyblue);
Shape2D::Rhombus(160, 120, Vec2{ 100, 500 }).draw(Palette::Skyblue);
// 吹き出しの長方形と、三角形の頂点の置を指定。三角形のサイズは pointingRootRatio で決まる
Shape2D::RectBalloon(RectF{ 220, 420, 160, 120 }, Vec2{ 220, 580 }).draw(Palette::Skyblue);
// base には階段の最も高い段の底の端の座標を指定。steps は段数、upStairs を false にすると下りの階段に
Shape2D::Stairs(Vec2{ 560, 560 }, 120, 120, 4).draw(Palette::Skyblue);
Shape2D::Heart(80, Vec2{ 700, 500 }).draw(Palette::Skyblue);
// 第 3 引数は角の丸の分割品質
Shape2D::Squircle(60, Vec2{ 900, 500 }, 64).draw(Palette::Skyblue);
}
}
3.16 自由に多角形を描く¶
Shape2D
では表現できない多角形を描くには Polygon
を作成して .draw()
します。Polygon
オブジェクトの作成には、メモリの確保や三角形分割の計算に少しだけ実行時コストがかかるため、ループの内側で作成するのは避けるべきです。Polygon
を作成するときは、各頂点の座標を時計回りに指定します。
# include <Siv3D.hpp>
void Main()
{
const Polygon polygon
{
Vec2{ 400, 100 }, Vec2{ 600, 300 }, Vec2{ 500, 500 }, Vec2{ 400, 400 }, Vec2{ 300, 500 }, Vec2{ 200, 300 }
};
while (System::Update())
{
polygon.draw(Palette::Skyblue);
}
}
3.17 穴の開いた多角形を描く¶
穴の開いた Polygon
を作るには、外周の時計回りの頂点座標リスト (Array<Vec2>
型) と、穴の形状の「反時計回り」の頂点座標リストの配列 (Array<Array<Vec2>>
型) から Polygon
を作成します。
# include <Siv3D.hpp>
void Main()
{
const Polygon polygon(
{ Vec2{ 400, 100 }, Vec2{ 600, 300 }, Vec2{ 500, 500 }, Vec2{ 400, 400 }, Vec2{ 300, 500 }, Vec2{ 200, 300 } },
{ { Vec2{ 450, 250 }, Vec2{ 350, 250 }, Vec2{ 350, 350 }, Vec2{ 450, 350 } } }
);
while (System::Update())
{
polygon.draw(Palette::Skyblue);
}
}
Polygon
よりも少ない実行時コストで図形を描きたい場合は、Shape2D
や Buffer2D
クラスの低レイヤ操作を使います。ただし、Shape2D
では、頂点配列のほかにインデックス配列を自前で用意する必要があり、Buffer2D
ではさらにテクスチャをマッピングするための UV 座標も必要になるなど、プログラムが複雑になります。本章では扱いません。
3.18 連続した線分を描く¶
連続した線分を描くには、Vec2
型の頂点の配列から LineString
を作成して .draw()
します。.drawClosed()
では終点と始点を結んだ線も描画されます。
# include <Siv3D.hpp>
void Main()
{
const LineString lineA
{
Vec2{ 100, 60 }, Vec2{ 400, 140 },
Vec2{ 100, 220 }, Vec2{ 400, 300 },
Vec2{ 100, 380 }, Vec2{ 400, 460 },
Vec2{ 100, 540 }
};
const LineString lineB
{
Vec2{ 500, 100 }, Vec2{ 700, 200 },
Vec2{ 600, 500 },
};
while (System::Update())
{
// 太さ 8px で描く
lineA.draw(8, Palette::Skyblue);
// 太さ 4px で描く(終点から始点も結ぶ)
lineB.drawClosed(4, Palette::Orange);
}
}
3.19 Catmull-Rom スプライン曲線を描く¶
指定した通過点を必ず通る Catmull-Rom スプライン曲線を描くには、 Spline2D
を作成して .draw()
します。Spline2D
は Vec2
の配列や LineString
から作成できます。コンストラクタの第 2 引数に Close::Ring
を指定することで、終点と始点がつながっているスプライン曲線を作成できます。
サンプルプログラムでは示していませんが、.draw()
には曲線計算時の品質(分割数)を指定する引数も用意されていて、デフォルトでは 24
になっています。
# include <Siv3D.hpp>
void Main()
{
const Spline2D splineA
{ {
Vec2{ 100, 60 }, Vec2{ 400, 140 },
Vec2{ 100, 220 }, Vec2{ 400, 300 },
Vec2{ 100, 380 }, Vec2{ 400, 460 },
Vec2{ 100, 540 }
} };
// CloseRing::Yes -> 終点から始点も結ぶ
const Spline2D splineB
{ {
Vec2{ 500, 100 }, Vec2{ 700, 200 },
Vec2{ 600, 500 },
}, CloseRing::Yes };
while (System::Update())
{
// 太さ 8px で描く
splineA.draw(8, Palette::Skyblue);
// 太さ 4px で描く
splineB.draw(4, Palette::Orange);
}
}
3.20 ベジェ曲線を描く¶
2 次ベジェ曲線を描きたいときは Bezier2
, 3 次ベジェ曲線を描きたいときは Bezier3
を作成して .draw()
します。
サンプルプログラムでは示していませんが、.draw()
には曲線計算時の品質(分割数)を指定する引数も用意されていて、デフォルトでは 24
になっています。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 2 次ベジェ曲線
Bezier2{ Vec2{ 100, 400 }, Vec2{ 100, 250 }, Vec2{ 300, 100 } }
.draw(4, Palette::Skyblue);
// 3 次ベジェ曲線
Bezier3{ Vec2{ 300, 400 }, Vec2{ 400, 400 }, Vec2{ 400, 100 }, Vec2{ 500, 100 }}
.draw(4, Palette::Orange);
}
}
3.21 矢印を描く¶
Line
には単方向の矢印を描く .drawArrow()
と、両方向の矢印を描く .drawDoubleHeadedArrow()
メンバ関数があります。いずれも第 1 引数には線の幅、第 2 引数には三角形の幅と高さを指定します。単方向矢印は、Line
の始点から終点方向を向きます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 線の幅 3px, 三角の幅 20px, 高さ 20px の単方向矢印を描く
Line{ 50, 200, 200, 250 }
.drawArrow(3, Vec2{ 20, 20 }, Palette::Skyblue);
// 線の幅 10px, 三角の幅 40px, 高さ 80px の単方向矢印を描く
Line{ 350, 450, 450, 100 }
.drawArrow(10, Vec2{ 40, 80 }, Palette::Orange);
// 線の幅 8px, 三角の幅 30px, 高さ 30px の両方向矢印を描く
Line{ 600, 100, 700, 400 }
.drawDoubleHeadedArrow(8, Vec2{ 30, 30 }, Palette::Limegreen);
}
}
3.22 扇形を描く¶
扇形を描くには、扇形のもとになる円 Circle
を作成し、.drawPie()
の引数に、12 時の方向を 0° とした時計回りの開始角度と、扇の角の大きさを指定します。.drawPie()
が元の図形を返すことを利用して、drawPie().drawPie()
のようにつなげたコードを書くこともできます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 開始角度 270° から 30° の大きさの扇形を描く
Circle{ 300, 300, 200 }
.drawPie(270_deg, 30_deg);
// 開始角度 0° から 120° の大きさの扇形と
// 開始角度 120° から 70° の大きさの扇形を描く
Circle{ 500, 300, 200 }
.drawPie(0_deg, 120_deg, Palette::Skyblue)
.drawPie(120_deg, 70_deg, Palette::Orange);
}
}
3.23 円弧を描く¶
円弧を描くには、円弧のもとになる円 Circle
を作成し、.drawArc()
の引数に、12 時の方向を 0° とした時計回りの開始角度と、扇の角の大きさ、弧の内側方向の太さ、外側方向の太さを指定します。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 開始角度 270° から 30° の大きさの円弧を描く
Circle{ 300, 300, 200 }
.drawArc(270_deg, 30_deg, 40, 0);
// 開始角度 0° から 120° の大きさの円弧と
// 開始角度 120° から 70° の大きさの円弧を描く
Circle{ 500, 300, 200 }
.drawArc(0_deg, 120_deg, 80, 0, Palette::Skyblue)
.drawArc(120_deg, 70_deg, 0, 20, Palette::Orange);
}
}
3.24 図形の操作¶
基準になる図形から、少しだけ変化させた形状を描きたいときに便利な機能を紹介します。
ほとんどの図形クラスが .movedBy()
メンバ関数を持ち、自身の座標を指定したベクトルで平行移動した図形を作成して返します。また、Rect
や Circle
, Line
など一部の図形クラスは .stretched()
メンバ関数を持ち、自身の幅や高さを変更した図形を作成して返します。
# include <Siv3D.hpp>
void Main()
{
constexpr Circle circle{ 100, 100, 60 };
constexpr Rect rect{ 400, 300, 200 };
while (System::Update())
{
circle.draw();
// (200, 0) の方向に平行移動
circle.movedBy(200, 0).draw(Palette::Skyblue);
// (0, 200) の方向に平行移動
circle.movedBy(0, 200).draw(Palette::Orange);
rect.drawFrame(2, 2);
// 上下左右を 10px 縮小
rect.stretched(-10).drawFrame(2, 2, Palette::Skyblue);
// 左右を 40px 拡大、上下を 20px 縮小
rect.stretched(40, -20).drawFrame(2, 2, Palette::Orange);
}
}
Polygon
は自身を拡大縮小した新しい Polygon
を返す .scaled()
や、回転した Polygon
を返す .rotated()
, .rotatedAt()
などのメンバ関数を持ちます。また、Shape2D
は Polygon
に変換可能です。
# include <Siv3D.hpp>
void Main()
{
const Polygon star = Shape2D::Star(150, Vec2{ 0, 0 });
while (System::Update())
{
star.scaled(1.2).movedBy(200, 200).draw(ColorF{ 0.6 });
star.movedBy(200, 200).draw(ColorF{ 0.8 });
star.scaled(0.8).movedBy(200, 200).draw(ColorF{ 1.0 });
star.rotated(-30_deg).movedBy(600, 400).draw(ColorF{ 0.6 });
star.movedBy(600, 400).draw(ColorF{ 0.8 });
star.rotated(30_deg).movedBy(600, 400).draw(ColorF{ 1.0 });
}
}
3.25 円 / 長方形 / 角丸長方形の影¶
Rect
, RectF
, Circle
, RoundRect
は、影を描画する .drawShadow()
メンバ関数を持っています。第 1 引数で影の位置のオフセット、第 2 引数でぼかしの大きさ、第 3 引数で影の大きさのオフセット、第 4 引数で影の色を指定できます。影は図形で隠れて見えない部分も塗りつぶされて描かれるため、影を描いたあとに上から図形を描く必要があります。
# include <Siv3D.hpp>
void Main()
{
Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });
while (System::Update())
{
Rect{ 100, 50, 150, 200 }
.drawShadow(Vec2{ 2, 2 }, 8, 1)
.draw();
Rect{ 300, 50, 150, 200 }
.drawShadow(Vec2{ 4, 4 }, 16, 2)
.draw();
Rect{ 500, 50, 150, 200 }
.drawShadow(Vec2{ 6, 6 }, 24, 3)
.draw();
Circle{ 100, 400, 50 }
.drawShadow(Vec2{ 0, 3 }, 8, 2)
.draw();
Circle{ 300, 400, 50 }
.drawShadow(Vec2{ 3, 0 }, 8, 2)
.draw();
Circle{ 500, 400, 50 }
.drawShadow(Vec2{ 0, -3 }, 8, 2)
.draw();
Circle{ 700, 400, 50 }
.drawShadow(Vec2{ -3, 0 }, 8, 2)
.draw();
}
}
これら以外の形状の影を作りたい場合は サンプル/図形や絵文字に影を付ける が参考になります。
3.26 グラデーション¶
Line
や Triangle
, Rect
, RectF
, Quad
には、頂点ごとに色を指定し、塗りつぶしの色をグラデーションにするオプションがあります。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 始点から終点にかけてグラデーション
Line{ 100, 100, 500, 150 }
.draw(6, Palette::Yellow, Palette::Red);
// 3 つの頂点の色でグラデーション
Triangle{ 200, 200, 100 }
.draw(HSV{ 0 }, HSV{ 120 }, HSV{ 240 });
// 左から右へのグラデーション
Rect{ 400, 200, 200, 100 }
.draw(Arg::left = Palette::Skyblue, Arg::right = Palette::Blue);
// 上から下へのグラデーション
Rect{ 200, 400, 400, 100 }
.draw(Arg::top = ColorF{ 1.0, 1.0 }, Arg::bottom = ColorF{ 1.0, 0.0 });
}
}