The images are generated on the fly by a custom route and controller in kc3_httpd. The xml body is then rendered with content type image/svg+xml.
This is an excerpt of our production code in app/controllers/svg_controller.kc3
:
defmodule SvgController do
def margin = 10
def points_on_circle = fn {
(n, size) { points_on_circle(n, size, n, []) }
(n, size, 0, acc) { List.reverse(acc) }
(n, size, i, acc) {
i = i - 1
a = 2 * F128.pi * i / n + F128.pi / 2
p = {(F128.cos(a) + 1) / 2 * size + margin,
(F128.sin(a) + 1) / 2 * size + margin}
points_on_circle(n, size, i, [p | acc])
}
}
def polygon = fn (request) {
name = File.name(request.url)
#puts("SvgController.polygon: name = #{name}")
if Str.starts_with?(name, "polygon_") do
name = Str.slice(name, 8, -1)
if [n, size, "svg"] = Str.split(name, ".") do
n = (U64) n
size = (U64) size
if (n > 1 && size > margin * 2) do
points = points_on_circle(n, size - margin * 2)
svg = SvgView.render_polygon(points, size)
%HTTP.Response{body: svg,
headers: [{"Content-Type", "image/svg+xml"}]}
end
end
end
}
def points_to_star = fn {
(points, n, k) { points_to_star(points, n, n - 1 - k, 0, []) }
(points, n, i, j, acc) {
acc = [points[i] | acc]
if (j == n) do
acc
else
i = if i < 2 do n + i - 2 else i - 2 end
points_to_star(points, n, i, j + 1, acc)
end
}
}
def star = fn (request) {
name = File.name(request.url)
puts("SvgController.star: name = #{name}")
if Str.starts_with?(name, "star_") do
name = Str.slice(name, 5, -1)
if [n, size, "svg"] = Str.split(name, ".") do
n = (U64) n
size = (U64) size
if (n > 1 && size > margin * 2) do
points = points_on_circle(n, size - margin * 2)
star_0 = points_to_star(points, n, 0)
star_1 = if (! (n mod 2)) do
points_to_star(points, n, 1)
else
[]
end
svg = SvgView.render_star([star_0, star_1], size)
%HTTP.Response{body: svg,
headers: [{"Content-Type", "image/svg+xml"}]}
end
end
end
}
def complete_graph = fn (request) {
name = File.name(request.url)
if Str.starts_with?(name, "complete_graph_") do
name = Str.slice(name, 15, -1)
if [n, size, "svg"] = Str.split(name, ".") do
n = (U64) n
size = (U64) size
if (n > 1 && size > margin * 2) do
points = points_on_circle(n, size - margin * 2)
svg = SvgView.render_complete_graph(points, size)
%HTTP.Response{body: svg,
headers: [{"Content-Type", "image/svg+xml"}]}
end
end
end
}
end
To understand better you should learn more Elixir because all the data model of KC3 is taken from Elixir. The main advantages of KC3 is that everything is introspectable through the graph database (triple store), and there is a much more simple and efficient KC3 / C interoperability with shared libraries through dlopen
and dlsym
.
We return a struct : %HTTP.Response{}
which is a KC3 struct compatible with C code. Unfortunately the KC3 <-> C interoperability is not portable and does not work on ARM64 (Apple Sillicon) because the padding and size of structs is different on each C compiler infrastructure. So it will always be a lot of work to follow each ABI on each system.