Skip to content

Instantly share code, notes, and snippets.

@aphix
Created May 29, 2025 03:01
Show Gist options
  • Select an option

  • Save aphix/83a799365f56c4fb8f514d19eb4e48db to your computer and use it in GitHub Desktop.

Select an option

Save aphix/83a799365f56c4fb8f514d19eb4e48db to your computer and use it in GitHub Desktop.
Karen Read Lexus Force Graph
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lexus Impact Physics Visualization · Stratified Efficiency Overlay</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body { font-family: Arial, sans-serif; background: #f4f4f4; }
h2 { text-align: center; }
.axis path, .axis line { fill: none; stroke: #000; }
.region-sweet { fill: #90ee90; opacity: 0.6; }
.region-shatter { fill: #ffffaa; opacity: 0.5; }
.region-bone { fill: #ffa0a0; opacity: 0.6; }
.curve { fill: none; stroke-width: 3; }
.curve-2 { stroke: #204ecf; }
.curve-5 { stroke: #30ae78; }
.curve-10 { stroke: #d1771f; }
.curve-20 { stroke: #f52276; }
.curve-100 { stroke: #000; stroke-dasharray: 4 3; stroke-width: 2; }
.legend rect { stroke: #666; stroke-width: 1; }
.curve-label {
font-size: 11px; font-weight: bold; text-shadow: 1px 1px #fff, -1px -1px #fff;
}
</style>
</head>
<body>
<h2>Lexus Impact: Force vs. Speed “Sweet Spot” (Stratified Efficiency)</h2>
<svg id="chart" width="900" height="540"></svg>
<div style="text-align: center;">
<small>
Colors = outcome regions. Lines = impact force by energy transfer efficiency (\(\eta\)).<br>
Dotted/black: 100% direct/perpendicular collision (theoretical max, highly unlikely).<br>
Blue: 2% efficiency (very glancing, shallow angle). Green: 5%. Orange: 10%. Pink: 20%.<br>
Legend shows typical biomechanical context for each angle/efficiency.<br>
Zones, thresholds, and text as before.
</small>
</div>
<script>
const MS_TO_MPH = 2.23694;
const MPH_TO_MS = 0.44704;
// Fixed parameters
const m_car = 2000; // kg
const d = 0.03; // m deformation
const F_bone = 3500; // N bone fracture
const F_taillight = 500; // N tail light shatter
const fatal_J = 50; // J
const effs = [
{eta:0.02, color:"curve-2", label:"2% efficiency — glancing (10–30°)", y_off:40 },
{eta:0.05, color:"curve-5", label:"5% — moderate oblique (20–40°)", y_off:30 },
{eta:0.10, color:"curve-10", label:"10% — firm but not square (30–60°)", y_off:40 },
{eta:0.20, color:"curve-20", label:"20% — nearly direct (60–90°)", y_off:55 },
{eta:1.00, color:"curve-100", label:"100% — perfectly direct blow (theoretical)", y_off:105 }
];
const svg = d3.select("#chart"),
margin = {top: 40, right: 90, bottom: 55, left: 75},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
const g = svg.append("g").attr("transform",`translate(${margin.left},${margin.top})`);
const mph_max = 35;
const x = d3.scaleLinear().domain([0, mph_max]).range([0, width]);
const y = d3.scaleLinear().domain([0, 8000]).range([height, 0]);
let mphTicks = d3.range(0, mph_max+1, 5);
g.append("g")
.attr("transform",`translate(0,${height})`)
.call(d3.axisBottom(x).tickValues(mphTicks))
.append("text")
.attr("y", 40).attr("x", width/2)
.attr("text-anchor","middle")
.attr("fill","#000")
.attr("font-weight", "bold")
.text("Speed (mph)");
g.append("g")
.call(d3.axisLeft(y).ticks(10))
.append("text")
.attr("transform","rotate(-90)")
.attr("y", -54)
.attr("x", -height/2)
.attr("text-anchor","middle")
.attr("fill","#000")
.attr("font-weight","bold")
.text("Avg Impact Force (N)");
// For regions, use the "10%" (common blunt force) to shade
function avForce(eta, mph) {
let v = mph * MPH_TO_MS;
return (eta * 0.5 * m_car * v * v) / d;
}
function avEnergy(eta, mph) {
let v = mph * MPH_TO_MS;
return eta * 0.5 * m_car * v * v;
}
const mphs = d3.range(0, mph_max+0.05, 0.05);
let regions = [];
for (let i=0; i<mphs.length-1; ++i) {
let force_10 = avForce(0.10, mphs[i]),
force_10p = avForce(0.10, mphs[i+1]),
E_10 = avEnergy(0.10, mphs[i]),
E_10p = avEnergy(0.10, mphs[i+1]);
let type;
if (force_10 >= F_bone || force_10p >= F_bone)
type = "bone";
else if ((force_10>=F_taillight||force_10p>=F_taillight) && (E_10>=fatal_J || E_10p>=fatal_J))
type = "sweet";
else
type = "shatter";
regions.push({
x0: x(mphs[i]), y0: y(force_10),
x1: x(mphs[i+1]), y1: y(force_10p),
type
});
}
regions.forEach(r=>{
if(r.type==="sweet")
g.append("polygon").attr("points",`${r.x0},${r.y0} ${r.x1},${r.y1} ${r.x1},${height} ${r.x0},${height}`).attr("class","region-sweet");
if(r.type==="bone")
g.append("polygon").attr("points",`${r.x0},${r.y0} ${r.x1},${r.y1} ${r.x1},${height} ${r.x0},${height}`).attr("class","region-bone");
if(r.type==="shatter")
g.append("polygon").attr("points",`${r.x0},${r.y0} ${r.x1},${r.y1} ${r.x1},${height} ${r.x0},${height}`).attr("class","region-shatter");
});
// Draw threshold lines
g.append("line")
.attr("x1",0).attr("x2",width)
.attr("y1",y(F_bone)).attr("y2",y(F_bone))
.attr("stroke", "#a00").attr("stroke-width", 2)
.attr("stroke-dasharray", "8 3");
g.append("line")
.attr("x1",0).attr("x2",width)
.attr("y1",y(F_taillight)).attr("y2",y(F_taillight))
.attr("stroke", "#888800").attr("stroke-width", 2)
.attr("stroke-dasharray", "8 3");
// Find speed for 50 J (at each efficiency)
function mphForEnergyJ(eta, J) {
let vms = Math.sqrt(J / (eta*0.5*m_car));
return vms * MS_TO_MPH;
}
let v_fatal = mphForEnergyJ(0.10, fatal_J);
g.append("line")
.attr("x1",x(v_fatal)).attr("x2",x(v_fatal))
.attr("y1",0).attr("y2",height)
.attr("stroke", "#006")
.attr("stroke-width", 2)
.attr("stroke-dasharray", "8 3");
// Labels
g.append("text")
.attr("x", x(24))
.attr("y", y(F_bone)-8)
.attr("fill", "#a00")
.attr("font-size", "12px")
.text("Bone Fracture Force (~3,500 N)");
g.append("text")
.attr("x", x(3))
.attr("y", y(F_taillight)-8)
.attr("fill", "#888800")
.attr("font-size", "12px")
.text("Tail Light Shatter Force (~500 N)");
g.append("text")
.attr("x", x(v_fatal)+4)
.attr("y", y(700))
.attr("fill", "#006")
.attr("font-size", "12px")
.attr("transform", `rotate(-90,${x(v_fatal)+4},${y(700)})`)
.text("Potentially Fatal Energy (~50 J, η=10%)");
// Draw force curves for all efficiencies
effs.forEach((e,ei)=>{
const curve = mphs.map(mph=>[x(mph), y(avForce(e.eta, mph))]);
g.append("path")
.attr("d", d3.line()(curve))
.attr("class", `curve ${e.color}`)
.attr("fill","none")
.attr("stroke-linecap","round")
.attr("stroke-linejoin","round");
// Label
let xm = x(32), ym = y(avForce(e.eta, 32));
g.append("text")
.attr("x", xm+8)
.attr("y", ym + e.y_off)
.attr("class","curve-label")
.attr("fill","black")
.attr("stroke","white")
.attr("paint-order","stroke")
.style("pointer-events","none")
.text(e.label);
});
// Legend
let legend = svg.append("g").attr("class","legend")
.attr("transform",`translate(${width-30},${margin.top+15})`);
legend.append("rect").attr("x",0).attr("y",0).attr("width",18).attr("height",18).attr("fill","#90ee90").attr("opacity",0.60);
legend.append("text").attr("x",25).attr("y",13).text("Sweet Spot").style("font-size","12px");
legend.append("rect").attr("x",0).attr("y",22).attr("width",18).attr("height",18).attr("fill","#ffa0a0").attr("opacity",0.60);
legend.append("text").attr("x",25).attr("y",35).text("Bone Fracture").style("font-size","12px");
legend.append("rect").attr("x",0).attr("y",44).attr("width",18).attr("height",18).attr("fill","#ffffaa").attr("opacity",0.60);
legend.append("text").attr("x",25).attr("y",57).text("Other/Neither").style("font-size","12px");
let l2 = svg.append("g").attr("class","legend")
.attr("transform",`translate(${width-25},${margin.top+92})`)
.style("font-size","11.5px");
l2.append("line")
.attr("x1",1).attr("x2",21).attr("y1",0).attr("y2",0)
.attr("class","curve curve-2");
l2.append("text").attr("x",25).attr("y",4).text("2% (Shallow) ");
l2.append("line")
.attr("x1",1).attr("x2",21).attr("y1",18).attr("y2",18)
.attr("class","curve curve-5");
l2.append("text").attr("x",25).attr("y",22).text("5% (Oblique) ");
l2.append("line")
.attr("x1",1).attr("x2",21).attr("y1",36).attr("y2",36)
.attr("class","curve curve-10");
l2.append("text").attr("x",25).attr("y",40).text("10% (Typical Blunt)");
l2.append("line")
.attr("x1",1).attr("x2",21).attr("y1",54).attr("y2",54)
.attr("class","curve curve-20");
l2.append("text").attr("x",25).attr("y",58).text("20% (Near-Direct)");
l2.append("line")
.attr("x1",1).attr("x2",21).attr("y1",72).attr("y2",72)
.attr("class","curve curve-100");
l2.append("text").attr("x",25).attr("y",76).text("100% (Direct Hit)");
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment