Published
Edited
Feb 19, 2021
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
computeValue = ({ x, a, v_0 }) => {
const aRad = (a * Math.PI) / 180;
const cos = Math.cos(aRad);
const y = x * Math.tan(aRad) - (9.82 * x * x) / (2 * v_0 * v_0 * cos * cos);
return y;
}
// computeValue = ({ v0, a, t }) => (t * (v0 + a * t)) / 2
Insert cell
createInput = (name, { min = 0, max = 1, step = 0.01, initialValue = 0.5 }) => {
const nValues = 100;
const innerHeight = height - margins.top - margins.bottom;
const innerWidth = width - margins.left - margins.right;
const yBase = height - margins.bottom;

const values = d3.range(min, max, (max - min) / nValues).concat(max);
const bandwidth = Math.max(1, Math.floor(innerWidth / nValues) - 1);
const halfMarkerWidth = 3 / 2;
const path = svg`<path fill="none" stroke="#e0e0e0" stroke-width=${bandwidth} />`;
const circle = svg`<circle fill="${resultColor}" r=5 />`;

const resultMarker = svg`<rect fill="${resultColor}" width=${2 *
halfMarkerWidth} y=${height - margins.bottom} />`;
const span = html`<span></span>`;
const inputLabel = html`<div class="input-label" />`;
const resultLabel = html`<div class="result-label"/>`;
const theSvg = svg`<svg viewbox="0 0 ${width} ${height}" style="cursor: col-resize">
${path}
${circle}
${resultMarker}
</svg>`;
const node = html`
<div class="input-graph" style="position: relative" >
${theSvg}
${inputLabel}
${resultLabel}
<h3 class="x-label">${tex`${name}`}</span>
</div>
`;

const xScale = d3
.scaleLinear([min, max], [margins.left, width - margins.right])
.clamp(true);
const yScale = d3.scaleLinear().range([height - margins.bottom, margins.top]);

const xAxisGenerator = d3.axisBottom(xScale);
const xAxisContainer = d3
.select(theSvg)
.append('g')
.attr("transform", `translate(${0}, ${height - margins.top})`)
.call(xAxisGenerator);

const yAxisGenerator = d3.axisRight(yScale).ticks(Math.round(height / 30));
const yAxisContainer = d3
.select(theSvg)
.append('g')
.attr("transform", `translate(${width - margins.right}, ${0})`)
.call(yAxisGenerator);

function update(event) {
const results = values.map(v => computeValueFor(name, v));
yScale.domain(d3.extent(results));
yAxisContainer.call(yAxisGenerator);

const value = viewof input.value[name];
const result = computeValueFor(name, value);

const x = xScale(value);
const y = yScale(result);
circle.setAttribute("cx", x);
circle.setAttribute("cy", y);

resultMarker.setAttribute("x", x - halfMarkerWidth);
resultMarker.setAttribute("y", y);
resultMarker.setAttribute("height", yBase - y);

inputLabel.innerText = value.toFixed(2);
inputLabel.style.left = x + "px";
inputLabel.style.top = height + "px";

resultLabel.innerText = result.toFixed(2);
resultLabel.style.left = x + "px";
resultLabel.style.top = y + "px";

// If the event originated from this one, we don't need to update the path
if (event.detail.name === name) return;

path.setAttribute(
"d",
values
.map(
(v, i) =>
`M${xScale(v)},${yBase}v${yScale(computeValueFor(name, v)) - yBase}`
)
.join("")
//"M" + values.map((v, i) => `${xScale(v)},${yScale(results[i])}`).join("L")
);
}

const updateByMouse = event =>
setInputValue(name, quantizeWithStep(xScale.invert(event.x), min, step));

d3.select(theSvg)
.call(d3.drag().on("drag", updateByMouse))
.on("pointerdown", updateByMouse);

const listenerArgs = [eventName, update];
viewof input.addEventListener(...listenerArgs);
invalidation.then(() => viewof input.removeEventListener(...listenerArgs));

update({ detail: { name: null } });
return node;
}
Insert cell
Insert cell
Insert cell
Insert cell
quantizeWithStep = (value, min, step) =>
Math.round((value - min) / step) * step + min
Insert cell
input
Insert cell
// TODO: refactor so we don't have to create so many objects when evaluating?
computeValueFor = (name, value) =>
computeValue({ ...viewof input.value, [name]: value })
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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