👁

Siv3D | データ可視化のサンプル集

2020/11/16に公開

有向グラフの可視化

# include <Siv3D.hpp> // OpenSiv3D v0.4.3

using NodeID = size_t;
inline constexpr int32 EllipseA = 60;
inline constexpr int32 EllipseB = 40;

struct Node
{
	NodeID id;

	String label;

	Vec2 pos;

	Ellipse getEllipse() const
	{
		return{ pos, EllipseA, EllipseB };
	}

	void draw() const
	{
		getEllipse()
			.draw()
			.drawFrame(2, Palette::Black);

		FontAsset(U"Node")(label)
			.drawAt(pos, Palette::Black);
	}
};

struct Edge
{
	NodeID from, to;
};

void DrawEdge(const Node& from, const Node& to)
{
	const Line line{ from.pos, to.pos };
	const Vec2 begin = line.intersectsAt(from.getEllipse()).value_or(Array<Vec2>{}).fetch(0, from.pos);
	const Vec2 end = line.intersectsAt(to.getEllipse()).value_or(Array<Vec2>{}).fetch(0, to.pos);
	Line{ begin, end }.drawArrow(3, Vec2{ 10, 20 }, Palette::Black);
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });
	FontAsset::Register(U"Node", 35, Typeface::Bold);

	HashTable<NodeID, Node> nodes;
	for (NodeID id = 0; id < 6; ++id)
	{
		nodes[id] = Node{ id, Format(char32(U'a' + id)), Scene::Center() };
	}

	const Array<Edge> edges
	{
		{ 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, { 4, 5 }, { 5, 0 },
		{ 0, 3 }
	};

	while (System::Update())
	{
		for (auto it = nodes.begin(); it != nodes.end(); ++it)
		{
			it.value().pos = OffsetCircular{ Scene::Center(), 200, (360_deg / 6) * it->first + Scene::Time() };
			it->second.draw();
		}

		for (const auto& edge : edges)
		{
			DrawEdge(nodes[edge.from], nodes[edge.to]);
		}
	}
}

3D の有向グラフの可視化

# include <Siv3D.hpp> // OpenSiv3D v0.4.3

using NodeID = size_t;

Float2 ToFloat2(Float3 v)
{
	v.x += 1.0f;
	v.y += 1.0f;
	v.x *= 0.5f * Scene::Width();
	v.y *= 0.5f;
	v.y = 1.0f - v.y;
	v.y *= Scene::Height();
	return v.xy();
}

struct Node
{
	NodeID id;

	String label;

	Vec3 pos;

	Sphere getSphere() const
	{
		return{ pos, 0.5 };
	}

	void draw(const Mat4x4& mat) const
	{
		getSphere()
			.draw(mat);
		
		const Float3 v = SIMD::Vector3TransformCoord(SIMD_Float4(pos, 1), mat).xyz();
		FontAsset(U"Node")(label)
			.drawAt(ToFloat2(v), Palette::Black);
	}
};

struct Edge
{
	NodeID from, to;
};

void DrawEdge(const Node& from, const Node& to, const Mat4x4& mat)
{
	const Ray rayTo{ from.pos, (to.pos - from.pos).normalized() };
	const Vec3 end = rayTo.origin + (rayTo.intersects(to.getSphere()).value_or(1.0f) * rayTo.direction);
	const Ray rayFrom{ to.pos, (from.pos - to.pos).normalized() };
	const Vec3 begin = rayFrom.origin + (rayFrom.intersects(from.getSphere()).value_or(1.0f) * rayFrom.direction);
	{
		constexpr size_t vertexCount = 2;
		const Float3 vec[vertexCount] = { begin, end };
		Float3 out[vertexCount];
		SIMD::Vector3TransformCoordStream(out, vec, vertexCount, mat);
		Line(ToFloat2(out[0]), ToFloat2(out[1]))
			.drawArrow(3, Vec2{ 10, 20 }, Palette::Black);
	}
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });
	FontAsset::Register(U"Node", 22, Typeface::Bold);

	constexpr double fov = 45_deg;
	constexpr Vec3 focusPosition{ 0, 0, 0 };
	Vec3 eyePosition{ 0, 10, 0 };
	BasicCamera3D camera{ Scene::Size(), fov, eyePosition, focusPosition };

	HashTable<NodeID, Node> nodes;
	nodes[0] = Node{ 0, U"a", Vec3(0, 6, 0) };
	nodes[1] = Node{ 1, U"b", Vec3(-4, 0.5, 0) };
	nodes[2] = Node{ 2, U"c", Vec3(4, 0.5, 0) };
	nodes[3] = Node{ 3, U"d", Vec3(0, 0.5, 8) };
	nodes[4] = Node{ 4, U"e", Vec3(0, 0.5, -8) };

	const Array<Edge> edges
	{
		{ 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 },
		{ 1, 2 }, { 1, 3 }, { 1, 4 }, { 2, 3 }, { 2, 4 }
	};

	while (System::Update())
	{
		eyePosition = Cylindrical{ 20, Scene::Time() * 30_deg, 8 + Periodic::Sine0_1(4s) * 8 };
		camera.setView(eyePosition, focusPosition);
		const Mat4x4 mat = camera.getMat4x4();
		{
			ScopedRenderStates2D culling(RasterizerState::SolidCullBack);

			for (auto i : Range(-10, 10))
			{
				Line3D(Vec3{ -10, 0, i }, Vec3{ 10, 0, i }).draw(mat, ColorF{ 0.5 });
				Line3D(Vec3{ i, 0, -10 }, Vec3{ i, 0, 10 }).draw(mat, ColorF{ 0.5 });
			}

			for (auto it = nodes.begin(); it != nodes.end(); ++it)
			{
				it->second.draw(mat);
			}

			for (const auto& edge : edges)
			{
				DrawEdge(nodes[edge.from], nodes[edge.to], mat);
			}
		}
	}
}

Discussion