Public
Edited
Aug 30, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const div = DOM.element("div");

function mkTrace(jetPnt, bombPnts, jetPnts) {
const n = bombPnts.length,
m = parseInt(n / 5),
data = [jetPnt, targetPnt, bombPnts[n - 1]],
bombTextPnts = bombPnts.filter((d, i) => i % m === 0),
traceLine = {
name: "Eyesight",
x: data.map((d) => d.x),
y: data.map((d) => d.y),
z: data.map((d) => d.z),
type: "scatter3d",
mode: "lines",
opacity: 1,
line: {
width: 3,
color: jetPnt.color
}
},
frameShip = {
name: "Ship",
x: shipDesign.map((d) => d.x),
y: shipDesign.map((d) => d.y),
z: shipDesign.map((d) => d.z),
type: "scatter3d",
mode: "lines",
opacity: 1,
line: {
width: 3,
color: "gray"
}
},
tracePnt = {
name: "Key points",
x: data.map((d) => d.x),
y: data.map((d) => d.y),
z: data.map((d) => d.z),
type: "scatter3d",
mode: "markers",
// opacity: 0.5,
marker: {
size: 5,
color: data.map((d) => (d.color ? d.color : "gray"))
}
},
traceBomb = {
name: "Bomb trace",
x: bombPnts.map((d) => d.x),
y: bombPnts.map((d) => d.y),
z: bombPnts.map((d) => d.z),
type: "scatter3d",
mode: "markers",
opacity: 0.1,
marker: {
size: 5,
color: d3.color(jetPnt.color).darker().hex() //"pink"
}
},
traceJet = {
name: "Jet trace",
x: jetPnts.map((d) => d.x),
y: jetPnts.map((d) => d.y),
z: jetPnts.map((d) => d.z),
type: "scatter3d",
mode: "markers",
opacity: 0.1,
marker: {
size: 3,
color: d3.color(jetPnt.color).darker().hex() //"pink"
}
},
traceBombText = {
name: "Bomb trace",
x: bombTextPnts.map((d) => d.x),
y: bombTextPnts.map((d) => d.y),
z: bombTextPnts.map((d) => d.z),
text: bombTextPnts.map((d) => d.t.toFixed(2)),
type: "scatter3d",
mode: "text",
opacity: 0.9
};

return [traceLine, tracePnt, traceBomb, traceJet, traceBombText, frameShip];
}

const traceIdeal = mkTrace(jetPnt, bombPnts, jetPnts),
traceTurning = mkTrace(turningJetPnt, turningBombPnts, []);

const trace = traceIdeal.concat(traceTurning);

Plotly.newPlot(div, trace, layout);

return div;
}
Insert cell
layout = {
const scene = {
camera: {
eye: { x: 2.87, y: 0.88, z: -0.64 }
},
aspectmode: aspectmode
};

return {
title: "Dive bombing trace",
scene,
autosize: false,
width: 600,
height: 600,
margin: {
l: 65,
r: 50,
b: 65,
t: 90
}
};
}
Insert cell
tankDesign = {
const length = meter2feet(4.7),
width = meter2feet(2),
height = meter2feet(2),
z = height,
rect = [
{ x: 0, y: 0, z },
{ x: length, y: 0, z },
{ x: length, y: width, z },
{ x: 0, y: width, z },
{ x: 0, y: 0, z }
];

rect.map((d) => {
d.x -= length / 2;
d.y -= width / 2;
});

return rect;
}
Insert cell
shipDesign = {
const length = meter2feet(300),
width = meter2feet(40),
height = meter2feet(20),
z = height,
rect = [
{ x: 0, y: 0, z },
{ x: length, y: 0, z },
{ x: length, y: width, z },
{ x: 0, y: width, z },
{ x: 0, y: 0, z }
];

rect.map((d) => {
d.x -= length / 2;
d.y -= width / 2;
});

return rect;
}
Insert cell
turningBombPnts = mkBombPnts(turningJetPnt)
Insert cell
turningBombPnts
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
jetPnts = mkJetPnts(jetPnt, bombPnts.length)
Insert cell
bombPnts = mkBombPnts(jetPnt)
Insert cell
bombPnts
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
mkJetPnts = (jetPnt, limit = 1e5) => {
const diffTime = 0.01, // Seconds
initSpeed = meter2feet((diveBombingSpeed * 1000) / 3600),
g = -4 * meter2feet(Gravity),
diff = {
x: targetPnt.x - jetPnt.x,
y: targetPnt.y - jetPnt.y,
z: targetPnt.z - jetPnt.z
};

const pnts = [];

var v = normVec3(diff, initSpeed),
{ x, y, z } = jetPnt;

for (let t = 0; t < 5; t += diffTime) {
pnts.push({ t, x, y, z, initSpeed, g });

x += v.x * diffTime;
y += v.y * diffTime;
z += v.z * diffTime;

v.z -= g * diffTime;

if (z < 0) break;

if (z > jetPnt.z) break;

limit -= 1;
if (limit < 0) break;
}

return pnts;
}
Insert cell
mkBombPnts = (jetPnt) => {
const diffTime = 0.01, // Seconds
initSpeed = meter2feet((diveBombingSpeed * 1000) / 3600),
g = meter2feet(Gravity),
diff = {
x: targetPnt.x - jetPnt.x,
y: targetPnt.y - jetPnt.y,
z: targetPnt.z - jetPnt.z
};

const pnts = [];

var v = normVec3(diff, initSpeed),
{ x, y, z } = jetPnt;

for (let t = 0; t < 5; t += diffTime) {
pnts.push({ t, x, y, z, initSpeed, g });

x += v.x * diffTime;
y += v.y * diffTime;
z += v.z * diffTime;

v.z -= g * diffTime;

if (z < 0) break;
}

return pnts;
}
Insert cell
turningJetPnt = {
const length = vec3ToLength(jetPnt),
newY = length * sin(turningAngle),
{ x, z } = jetPnt;

return { x, y: newY, z, color: "blue" };
}
Insert cell
jetPnt = {
const z = diveBombingHeight,
x = z / tan(diveBombingAngle),
y = 0;

return { x, y, z, color: "red" };
}
Insert cell
targetPnt = {
return { x: 0, y: 0, z: 0, color: "darkgreen" };
}
Insert cell
Gravity = 9.8
Insert cell
meter2feet = feet2meter.invert
Insert cell
feet2meter = d3
.scaleLinear()
.domain([0, 1])
.range([0, MilesPerFeet * 1000])
Insert cell
MilesPerFeet = 0.000189393939
Insert cell
Insert cell
d3 = require("d3")
Insert cell
Plotly = require("https://cdn.plot.ly/plotly-2.20.0.min.js")
Insert cell
/**
* Compute the length of the vec3
*/
vec3ToLength = (vec3) => {
const { x, y, z } = vec3,
length = Math.sqrt(x ** 2 + y ** 2 + z ** 2);

return length;
}
Insert cell
/**
* Re length the vec3 into norm=n
*/
normVec3 = (vec3, n = 1) => {
const { x, y, z } = vec3,
length = vec3ToLength(vec3),
r = n / length;

return { x: x * r, y: y * r, z: z * r };
}
Insert cell
tan = (degree) => {
return Math.tan((degree / 180) * Math.PI);
}
Insert cell
sin = (degree) => {
return Math.sin((degree / 180) * Math.PI);
}
Insert cell
cos = (degree) => {
return Math.cos((degree / 180) * Math.PI);
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more