👁
Siv3D | データ可視化のサンプル集
有向グラフの可視化
# 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