Jarvis/jarvis/objective/templates/skills/learning_line.html

194 lines
6.9 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block header %}
<style>
svg {
/* width: 50%;
height: 50%; */
width: 25%;
height: 25%;
display: block;
margin: auto;
}
</style>
{% endblock %}
{% block content %}
<div class="card mb-0">
<div class="card-header">
<h3 class="mb-0">{{ skill.short_label }}</h3>
<h4 class="card-title"> {{ skill.notation }}</h4>
</div>
<div class="card-body pb-0 mb-0">
<div class="row mr-1 ml-1 pb-0 mb-0">
<svg></svg>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script src="{% static "js/plugins/D3-dag/d3_7.6.1.min.js" %}"></script>
<script src="{% static "js/plugins/D3-dag/d3-dag_0.11.1.min.js" %}"></script>
<script>
function compute_font_size(string)
{
number_of_spaces = string.split(/ /g).length - 1;
number_of_tiny_char = (string.split(/f/g).length - 1)
+ (string.split(/i/g).length - 1)
+ (string.split(/j/g).length - 1)
+ (string.split(/l/g).length - 1)
+ (string.split(/t/g).length - 1)
+ (string.split(/\//g).length - 1)
+ (string.split(/,/g).length - 1)
+ (string.split(/\(/g).length - 1)
+ (string.split(/\)/g).length - 1)
number_of_small_char = (string.split(/r/g).length - 1);
number_of_big_char = (string.split(/½/g).length - 1)
+ (string.split(/¾/g).length - 1)
+ (string.split(/B/g).length - 1)
+ (string.split(/H/g).length - 1)
+ (string.split(/O/g).length - 1)
+ (string.split(/R/g).length - 1)
+ (string.split(/T/g).length - 1)
+ (string.split(/</g).length - 1);
return string.length
+ (number_of_big_char / 2)
- (
number_of_spaces
+ (number_of_tiny_char / 1.5)
+ (number_of_small_char / 2)
);
}
function define_font_size(string)
{
return (1 / compute_font_size(string)) * 80;
}
(async () => {
const data = [
{% for key, value in node_dict.items %}
{
"id": "{{ key.id }}",
"label": "{% autoescape off %}{{ key.short_label }}{% endautoescape %}",
"parentIds": [
{% for prerequisite in value %}"{{ prerequisite.id }}", {% endfor %}
]
},{% endfor %}
]
const dag = d3.dagStratify()(data);
const nodeRadius = 30;
const layout = d3
//
// Base layout
.sugiyama()
//
// Layering
.layering(d3.layeringSimplex()) // Simplex (shortest edges)
// .layering(d3.layeringLongestPath()) // Longest Path (minimum height)
// .layering(d3.layeringCoffmanGraham()) // Coffman Graham (constrained width)
//
// Decrossing
.decross(d3.decrossOpt()) // Optimal (can be very slow)
// .decross(d3.decrossTwoLayer().order(d3.twolayerAgg())) // Two Layer Agg (fast)
// .decross(d3.decrossTwoLayer().order(d3.twolayerOpt())) // Two Layer Opt (can be very slow)
//
// Coords
// .coord(d3.coordCenter()) // Center (fast)
.coord(d3.coordGreedy()) // Greedy (fast)
// .coord(d3.coordQuad()) // Quadradtic (can be slow)
// .coord(d3.coordTopological()) //
.nodeSize(
(node) => [(node ? 3.6 : 0.25) * nodeRadius, 3 * nodeRadius]
); // set node size instead of constraining to fit
// .nodeSize((node) => {
// const size = node ? base : 5;
// return [1.2 * size, size];
// });
// .attr("r", function(d) {return d.name.length * 2.5;})
const { width, height } = layout(dag);
// --------------------------------
// This code only handles rendering
// --------------------------------
const svgSelection = d3.select("svg");
svgSelection.attr("viewBox", [0, 0, width, height].join(" "));
const defs = svgSelection.append("defs"); // For gradients
const steps = dag.size();
const interp = d3.interpolateRainbow;
const colorMap = new Map();
for (const [i, node] of [...dag].entries()) {
colorMap.set(node.data.id, interp(i / steps));
}
// How to draw edges
const line = d3
.line()
.curve(d3.curveCatmullRom)
.x((d) => d.x)
.y((d) => d.y);
// Plot edges
svgSelection
.append("g")
.selectAll("path")
.data(dag.links())
.enter()
.append("path")
.attr("d", ({ points }) => line(points))
.attr("fill", "none")
.attr("stroke-width", 3)
.attr("stroke", ({ source, target }) => {
// encodeURIComponents for spaces, hope id doesn't have a `--` in it
const gradId = encodeURIComponent(`${source.data.id}--${target.data.id}`);
const grad = defs
.append("linearGradient")
.attr("id", gradId)
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", source.x)
.attr("x2", target.x)
.attr("y1", source.y)
.attr("y2", target.y);
grad
.append("stop")
.attr("offset", "0%")
.attr("stop-color", colorMap.get(source.data.id));
grad
.append("stop")
.attr("offset", "100%")
.attr("stop-color", colorMap.get(target.data.id));
return `url(#${gradId})`;
});
// Select nodes
const nodes = svgSelection
.append("g")
.selectAll("g")
.data(dag.descendants())
.enter()
.append("g")
.attr("transform", ({ x, y }) => `translate(${x}, ${y})`);
// Plot node circles
nodes
.append("circle")
.attr("r", nodeRadius)
.attr("fill", (n) => colorMap.get(n.data.id));
// Add text to nodes
nodes
.append("text")
.text((d) => d.data.label)
.attr("font-size", function(d) {return Math.min(define_font_size(d.data.label), 24);})
.attr("font-weight", "bold")
.attr("font-family", "sans-serif")
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.attr("fill", "white");
})();
</script>
{% endblock %}