console.clear();
var ttSel = d3.select("body").selectAppend("div.tooltip.tooltip-hidden");
// For result tables
const columns = ["object", "n", "n correct", "accuracy"];
const rowHeight = 50;
const rowWidth = 100;
const buffer = 2;
const classifierBlobWidth = 50;
const classifierBlobHeight = 460;
function drawShapesWithData(classifier) {
var divHeight = classifier.class == "show-shapes" ? 250 : 490;
var c = d3.conventions({
sel: d3.select("." + classifier.class).html(""),
width: 1300,
height: divHeight,
layers: "ds",
});
function runClassifier() {
classifier.isClassified = true;
var duration = 3000;
classifierSel.classed("is-classified", true);
graphResultsGroup.classed("is-classified", true);
drawResults();
buttonSel.text("Reset");
var minX = d3.min(shapeParams, (d) => d.endX - 50);
var timer = d3.timer((ms) => {
if (!classifier.isClassified) {
timer.stop();
shapeSel.classed("is-classified", false);
return;
}
var t = d3.easeCubicInOut(ms / duration);
t = d3.clamp(0, t, 1);
shapeParams.forEach((d, i) => {
d.x = d.startX + (d.endX - d.startX) * t;
d.y = d.startY + (d.endY - d.startY) * t;
d.isClassified = d.x > minX;
});
shapeSel
.translate((d) => [d.x, d.y])
.classed("is-classified", (d) => d.isClassified);
if (t == 1) {
timer.stop();
}
});
}
function resetClassifier() {
shapeSel.translate((d) => [d.startX, d.startY]);
shapeSel.classed("is-classified", false);
classifier.isClassified = false;
shapeSel
.transition("position")
.duration(0)
.translate((d) => [d.startX, d.startY]);
classifierSel.classed("is-classified", false);
graphResultsGroup.classed("is-classified", false);
if (classifier.class != "show-shapes") {
classifierBlobSel.attr("opacity", 100);
}
drawResults();
buttonSel.text("Run Classifier");
}
// Add run/reset button
var buttonSel = d3
.select("." + classifier.class + "-button")
.html("")
.append("button#run")
.at({
type: "button",
class: "classifier-button",
})
.text("Run Classifier")
.on("click", () => {
// if already classified, reset
if (classifier.isClassified) {
// Resetting
resetClassifier();
} else {
runClassifier();
}
});
// Backgrounds for different classifications
var classifierSel = c.svg
.append("g")
.at({
class: "classifier",
})
.translate([465, 20]);
classifierSel
.append("path.classifier-bg-shaded")
.at({
d: classifierBgPathTop,
// fill: "#ccc",
// stroke: "#000",
})
.translate([-50, 0]);
classifierSel
.append("text.classifier-bg-text")
.at({
fill: "#000",
textAnchor: "middle",
dominantBaseline: "central",
class: "monospace",
})
.text("shaded")
.translate([160, 15]);
classifierSel
.append("path.classifier-bg-unshaded")
.at({
d: classifierBgPathBottom,
})
.translate([-50, 160]);
classifierSel
.append("text.classifier-bg-text")
.at({
fill: "#000",
textAnchor: "middle",
dominantBaseline: "central",
class: "monospace",
})
.text("unshaded")
.translate([160, 175]);
// Add the shapes themselves
var shapeSel = c.svg
.appendMany("path.shape", shapeParams)
.at({
d: (d) => d.path,
class: (d) => "gt-" + d.gt + " " + d.correctness,
})
.translate(function (d) {
if (classifier.class == "show-shapes") {
return [d.initialX + 35, d.initialY-20];
} else {
return [d.startX, d.startY];
}
})
.call(d3.attachTooltip)
.on("mouseover", (d) => {
ttSel.html("");
if (classifier.usingLabel != "none") {
ttSel
.append("div")
.html(
`labeled: ${toPropertyString(
d[classifier.usingLabel],
classifier.isRounding
).slice(0, -1)}`
);
}
var gtSel = ttSel
.append("div")
.html(
`ground truth: ${d.gt}`
);
if (classifier.isClassified) {
ttSel
.append("div.labeled-row")
.html(
`classified as: ${d.label}`
);
ttSel
.append("div.correct-row")
.classed("is-correct-tooltip", d.correctness == "correct")
.html(`
${d.correctness}ly classified `);
}
ttSel.classed("tt-text", true);
});
// If we're just showing shapes, ignore everything else
if (classifier.class == "show-shapes") return;
// Add "classifier" line
var classifierBlobSel = c.svg
.append("g")
.at({
class: "classifier-blob",
strokeWidth: 0,
})
.translate([378, 20]);
classifierBlobSel
.append("line.classifier-blob")
.at({
class: "line",
x1: 27,
x2: 27,
y1: 0,
y2: 464,
stroke: "#000",
strokeWidth: 1,
})
.style("stroke-dasharray", "5, 5");
classifierBlobSel
.append("text.classifier-blob-text")
.at({
class: "classifier-blob-text monospace",
textAnchor: "middle",
dominantBaseline: "central",
})
.text("is_shaded classifier")
.attr("transform", "translate(30,480) rotate(0)");
if (classifier.class == "show-shapes") {
classifierBlobSel.classed("is-classified", true);
}
// Draw the results table with accuracies
// This will be hidden before classifier is run.
var graphResultsGroup = c.svg
.append("g")
.attr("class", "results")
.translate([-20, 19]);
function drawResults() {
// Write text summary
summarySel = d3
.select("." + classifier.class + "-summary")
.html(summaries[classifier.class])
.translate([0, 20]);
summarySel.classed("summary-text", true);
summarySel.classed("is-classified", classifier.isClassified);
if (!classifier.isClassified) {
c.layers[0].html("");
classifier.wasClassified = false;
return;
}
// Access results, which are calculated in shapes.js.
// If there are none, draw nothing.
results = allResults[classifier.class];
if (!results) return;
// Figure out which shapes should be highlighted on mouseover
// This depends on whether we're "rounding" edge case examples.
function isMatch(rowName, labelName, isRounding) {
// Not filtering at all
if (rowName == "shape") {
return true;
}
if (isRounding == true) {
// No "other" category
return labelName.includes(toOriginalString(rowName))
? true
: false;
} else {
// There is an "other" category, prefixed by "rt_"
if (labelName == toOriginalString(rowName)) {
return true;
} else if (
labelName.includes("rt_") &&
rowName == "other shapes"
) {
return true;
}
return false;
}
}
// Color the last row of each table
function getColor(d, i) {
if (i != 3) {
// not last index
return "#e6e6e6";
} else {
var scaleRowValue = d3
.scaleLinear()
.domain([0.3, 1.0])
.range([0, 1]);
return d3.interpolateRdYlGn(scaleRowValue(d));
}
}
// Adjust text color for visibility
function getTextColor(d, i) {
if (i != 3) {
// not last index
return "#000000";
} else {
var bgColor = getColor(d, i);
if (d < 0.3) {
// Alternative: use a brighter color?
// return d3.rgb(bgColor).brighter(-2);
return "#FFCCD8";
} else {
// Alternative: use a darker color?
// return d3.rgb(bgColor).darker(2);
return "#000000";
}
}
}
// Draw results table
var tableSel = c.layers[0]
.html("")
.raise()
.st({ width: 400 })
.append("div")
.translate([0, 10])
.append("table.results-table.monospace")
.st({ width: 400 });
var header = tableSel
.append("thead")
.append("tr")
.appendMany("th", columns)
.text((d) => d);
var rowSel = tableSel
.appendMany("tr", results)
.at({
class: "row monospace",
})
.on("mouseover", (row) => {
if (classifier.class == "default-classifier") {
return;
}
rowSel.classed("active", (d) => d == row);
shapeSel.classed("shape-row-unhighlighted", function (d) {
return !isMatch(
row.object,
d[classifier.usingLabel],
(isRounding = classifier.isRounding)
);
});
})
.on("mouseout", (row) => {
rowSel.classed("active", function (d) {
if (d == row) {
return false;
}
});
if (classifier.isClassified) {
shapeSel.classed("shape-row-unhighlighted", 0);
}
});
rowSel
.appendMany("td", (result) =>
columns.map((column) => result[column])
)
.text((d) => d)
.st({
backgroundColor: getColor,
color: getTextColor,
});
header.style("opacity", 0);
rowSel.style("opacity", 0);
// If the classifier has already been run before, draw results right away.
// Otherwise, wait for other animation to run before drawing results.
var initialDelay = classifier.wasClassified ? 0 : 2000;
classifier.wasClassified = true;
header
.transition()
.delay(initialDelay)
.duration(1000)
.style("opacity", 1);
rowSel
.transition()
.delay(function (d, i) {
return initialDelay + i * 200;
})
.duration(1000)
.style("opacity", 1);
}
// Draw the dropdowns for selecting different labels
function drawDropdown() {
if (!classifier.options) return;
["rounding", "category"].forEach(function (classifierType) {
if (!classifier.options[classifierType]) return;
var sel = d3
.select("#" + classifier.class + "-select-" + classifierType)
.html("");
sel.classed("dropdown", true);
sel.appendMany("option", classifier.options[classifierType])
.at({
value: function (d) {
return d.value;
},
})
.text((d) => d.label);
sel.on("change", function () {
if (classifierType == "rounding") {
classifier.isRounding = toBool(this.value);
} else {
classifier.usingLabel = this.value;
}
updateResults();
drawResults();
});
});
}
drawDropdown();
updateResults();
drawResults();
// For continuity, auto-run the second two classifiers
if (
classifier.class == "second-classifier" ||
classifier.class == "final-classifier"
) {
runClassifier();
}
}
// Draw the "Labels Tell Stories" section
function drawConclusion() {
function drawNewspapers() {
d3.select(".conclusion-newspapers").html(function () {
var imgPath =
"img/newspapers_" +
document.getElementById("conclusion-select-category").value;
return (
''
);
});
}
function drawInterface() {
d3.select(".conclusion-interface").html(function () {
var imgPath =
"img/confusing_" +
document.getElementById("conclusion-select-category").value;
return (
'