viewof rayANDsphere = {
let ray = {
direction: [1, 1],
origin: [0, 0],
tmin: 0,
tmax: Number.POSITIVE_INFINITY
};
let sphere = {
center: [3, 3],
radius: 2
};
let rayANDsphere = { ray, sphere };
const intersectSphere = () => {
ray.tmin = 0;
ray.tmax = Number.POSITIVE_INFINITY;
return ray_sphere(ray, sphere);
};
const svgnode = DOM.svg(width, height);
const svg = d3.select(svgnode);
let direction = ray.direction;
let origin = ray.origin;
let sphereIntersected = ray_sphere(ray, sphere);
svgnode.value = rayANDsphere;
const g = svg;
const circle = g
.append("circle")
.datum({
cx: xScale(sphere.center[0]),
cy: yScale(sphere.center[1]),
r: xScale(sphere.center[0] + sphere.radius) - xScale(sphere.center[0])
})
.attr("cursor", "move")
.attr("cx", d => d.cx)
.attr("cy", d => d.cy)
.attr("r", d => d.r)
.style("fill-opacity", 0.1)
.style("stroke", "black")
.call(d3.drag().on("drag", draggedSphere));
const mainVector = g
.append("polyline")
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("marker-end", "url(#arrow)");
const rayForward = g
.append("polyline")
.attr("stroke", "magenta")
.attr("stroke-width", 1);
const rayBackward = g
.append("polyline")
.attr("stroke", "red")
.attr("opacity", 0.25)
.attr("stroke-width", 1)
.attr("stroke-dasharray", "4");
const rayRange = g
.append("polyline")
.attr("stroke", "black")
.attr("stroke-width", 2);
const rayRangePoints = g
.append("g")
.attr("stroke", "black")
.attr("fill", "none")
.attr("stroke-width", 1);
const rayRangeBegin = rayRangePoints.append("circle").attr("r", 3);
const rayRangeEnd = rayRangePoints.append("circle").attr("r", 3);
const label = g
.append("g")
.style("font-style", "italic")
.style("font-size", 11);
const labelTmin = label.append("text");
const labelTmax = label.append("text");
const point = g
.append("g")
.attr("cursor", "move")
//.attr("pointer-events", "all")
.attr("stroke", "transparent")
.attr("stroke-width", 5)
.append("circle")
.datum(scale(direction))
.attr("r", 2.5)
.call(d3.drag().on("drag", draggedRayHead));
update(scale(direction));
function draggedSphere(event, d) {
d.cx = Math.max(0, Math.min(width - d.r, event.x)); //event.x;
d.cy = Math.max(0, Math.min(height - d.r, event.y)); //event.y;
circle
.raise()
.attr("cx", d.cx)
.attr("cy", d.cy);
sphere.center[0] = xScale.invert(d.cx);
sphere.center[1] = yScale.invert(d.cy);
svgnode.dispatchEvent(new CustomEvent("input"));
update(d, true);
}
function draggedRayHead(event, d) {
d[0] = event.x;
d[1] = event.y;
direction[0] = xScale.invert(d[0]);
direction[1] = yScale.invert(d[1]);
d3.select(this).raise();
svgnode.dispatchEvent(new CustomEvent("input"));
update(d);
}
function pointOnRay(t) {
return [origin[0] + t * direction[0], origin[1] + t * direction[1]];
}
function pForward() {
return pointOnRay(1000);
}
function pBackward() {
return pointOnRay(-1000);
}
function update(d, sphereFlag) {
const sphereIntersected = intersectSphere();
let p_tMin = scale(pointOnRay(ray.tmin)),
p_tMax = scale(pointOnRay(ray.tmax));
if (!sphereFlag) point.attr("cx", d[0]).attr("cy", d[1]);
rayForward.attr("points", [scale(origin), scale(pForward())]);
rayBackward.attr("points", [scale(pBackward()), scale(origin)]);
if (sphereIntersected) {
label
.style("text-anchor", direction[0] <= 0 ? "start" : "end")
.style("alignment-baseline", direction[1] <= 0 ? "auto" : "hanging")
.style("opacity", 1);
labelTmin
.attr("transform", `translate(${p_tMin})`)
.attr("dx", direction[0] >= 0 ? windowExtent.x[0] : windowExtent.x[1])
.attr("dy", direction[1] >= 0 ? windowExtent.y[0] : windowExtent.y[1])
.text(`t = ${d3.format(".1f")(ray.tmin)}`);
labelTmax
.attr("transform", `translate(${p_tMax})`)
.attr("dx", direction[0] >= 0 ? windowExtent.x[0] : windowExtent.x[1])
.attr("dy", direction[1] >= 0 ? windowExtent.y[0] : windowExtent.y[1])
.text(`t = ${d3.format(".1f")(ray.tmax)}`);
rayRange.style("opacity", 1);
rayRangePoints.style("opacity", 1);
rayRange.attr("points", [p_tMin, p_tMax]);
rayRangeBegin
.datum(p_tMin)
.attr("cx", d => d[0])
.attr("cy", d => d[1]);
rayRangeEnd
.datum(p_tMax)
.attr("cx", d => d[0])
.attr("cy", d => d[1]);
} else {
label.style("opacity", 0);
rayRange.style("opacity", 0);
rayRangePoints.style("opacity", 0);
}
if (!sphereFlag) mainVector.attr("points", [scale(origin), d]);
}
return svgnode;
}