JSFiddle - React, Tailwind, and code Playground
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Iron Triangle Visualizer</title>
<link rel="stylesheet" href="styles.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<header>
<h1>Iron Triangle Visualizer</h1>
</header>
<section class="main-container">
<nav class="selector-container">
<div class="toggle-radio">
<input type="radio" name="good" id="good" value="good">
<label for="good">Good</label>
</div>
<br>
<div class="toggle-radio">
<input type="radio" name="fast" id="fast" value="fast">
<label for="fast">Fast</label>
</div>
<br>
<div class="toggle-radio">
<input type="radio" name="cheap" id="cheap" value="cheap">
<label for="cheap">Cheap</label>
</div>
</nav>
<section class="triangle-container" id="tri-interp-m2">
<div id="tri-int-div" class="tri-int-div">
<svg class="tri-interp-svg">
<line class="primary-line" x1="175" y1="75" x2="75" y2="275"></line>
<line class="primary-line" x1="175" y1="75" x2="275" y2="275"></line>
<line class="primary-line" x1="75" y1="275" x2="275" y2="275"></line>
<line class="secondary-line" id="tri-interp-m2-l1" x1="175" y1="75" x2="175" y2="175"></line>
<line class="secondary-line" id="tri-interp-m2-l2" x1="75" y1="275" x2="175" y2="175"></line>
<line class="secondary-line" id="tri-interp-m2-l3" x1="275" y1="275" x2="175" y2="175"></line>
<circle class="circle-accent-1" cx="175" cy="75" r="10"></circle>
<circle class="circle-accent-2" cx="75" cy="275" r="10"></circle>
<circle class="circle-accent-3" cx="275" cy="275" r="10"></circle>
<text class="text-good" x="200" y="84">Good</text>
...
CSS
body, html {
margin: 0;
height: 100%;
}
body {
background-color: #222;
color: #f0f0f0;
font-family: Arial, sans-serif;
}
header, footer {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
header {
background-color: #444;
}
.main-container {
display: flex;
justify-content: space-around;
align-items: center;
min-height: calc(100% - 200px);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
padding: 1rem;
}
.selector-container {
width: 33%;
padding-right: 10px;
display: flex;
flex-direction: column;
align-items: flex-end;
}
.triangle-container {
width: 350px;
height: 350px;
display: flex;
justify-content: center;
align-items: center;
}
.barchart-container {
width: 33%;
min-width: 120px;
height: 300px;
padding-left: 10px;
display: flex;
justify-content: center;
align-items: flex-start;
flex-direction: column;
}
.tri-int-div {
border: 1px solid grey;
width: 350px;
height: 350px;
margin-left: auto;
margin-right: auto;
margin-bottom: 0.5em;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
.tri-interp-svg {
width: 350px;
height: 350px;
}
.toggle-radio {
display: inline-block;
position: relative;
padding: 0;
margin-right: 10px;
width: 120px;
height: 40px;
background-color: #ccc;
border-radius: 15px;
}
.toggle-radio label {
display: block;
position: absolute;
top: 0;
left: 0;
width: 50%;
height: 40px;
background-color: grey;
border-radius: 15px;
cursor: pointer;
transition: left 0.3s;
line-height: 40px;
text-align: center;
font-weight: bold;
color: white;
}
.toggle-radio input {
display: none;
}
.toggle-radio input:checked + label {
left: 50%;
}
.toggle-radio input[type="radio"]#good:checked + label {
background-color: purple;
}
.toggle-radio input[type="radio"]#fast:checked + label {
background-color: red;
}
.toggle-radio...
JavaScript
var calc_barycentric_weights = function(x, y, v1x, v1y, v2x, v2y, v3x, v3y) {
var w_v1 = (x * (v3y - v2y) + v2x * (y - v3y) + v3x * (v2y - y)) / (v1x * (v3y - v2y) + v2x * (v1y - v3y) + v3x * (v2y - v1y));
var w_v2 = -(x * (v3y - v1y) + v1x * (y - v3y) + v3x * (v1y - y)) / (v1x * (v3y - v2y) + v2x * (v1y - v3y) + v3x * (v2y - v1y));
var w_v3 = 1.0 - w_v1 - w_v2;
return [w_v1, w_v2, w_v3];
}
var update_with_bary = function(x, y) {
var p = document.getElementById("tri-interp-m2-p");
var l1 = document.getElementById("tri-interp-m2-l1");
var l2 = document.getElementById("tri-interp-m2-l2");
var l3 = document.getElementById("tri-interp-m2-l3");
var w1 = document.getElementById("tri-interp-m2-w1");
var w2 = document.getElementById("tri-interp-m2-w2");
var w3 = document.getElementById("tri-interp-m2-w3");
var weights = calc_barycentric_weights(x, y, 175, 75, 75, 275, 275, 275);
if (weights[0] < 0 || weights[1] < 0 || weights[2] < 0) return;
p.setAttribute("cx", x);
p.setAttribute("cy", y);
var w_v1 = weights[0];
var w_v2 = weights[1];
var w_v3 = weights[2];
l1.setAttribute("x2", x);
l2.setAttribute("x2", x);
l3.setAttribute("x2", x);
l1.setAttribute("y2", y);
l2.setAttribute("y2", y);
l3.setAttribute("y2", y);
l1.style['stroke-width'] = (w_v1 * 5) + 1 + "px";
l2.style['stroke-width'] = (w_v2 * 5) + 1 + "px";
l3.style['stroke-width'] = (w_v3 * 5) + 1 + "px";
w1.textContent = Math.round(w_v1 * 100) + "%";
w2.textContent = Math.round(w_v2 * 100) + "%";
w3.textContent = Math.round(w_v3 * 100) + "%";
barChart.data.datasets[0].data = [w_v1 * 100, w_v2 * 100, w_v3 * 100];
barChart.update();
};
function updateBarGraph(good, fast, cheap) {
var barGraph = document.getElementById("bar-graph");
var barGood = document.getElementById("bar-good");
var barFast = document.getElementById("bar-fast");
var barCheap = document.getElementById("bar-cheap");
barGood.style.height = good * 100 + "px";
...