|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<style> |
|
body { |
|
margin: 0; |
|
} |
|
line { |
|
stroke-width: 2px; |
|
pointer-events: none; |
|
} |
|
.surface-line { |
|
stroke: black; |
|
} |
|
.start-angle { |
|
stroke: steelblue; |
|
} |
|
.reflect-angle { |
|
stroke: purple; |
|
} |
|
circle { |
|
stroke: #fff; |
|
cursor: pointer; |
|
} |
|
.start-handle { |
|
fill: steelblue; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<script src="https://d3js.org/d3.v5.min.js"></script> |
|
<script src="https://unpkg.com/geometric@1.0.6/build/geometric.js"></script> |
|
<script> |
|
var width = window.innerWidth, height = window.innerHeight; |
|
var center = [width / 2, height / 2]; |
|
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height); |
|
|
|
var surfaceAnglePrev = 0, |
|
surfaceAngle = 0, |
|
surfaceAngleChange = 0, |
|
surfaceStartPoint, |
|
surfaceEndPoint, |
|
startAngle = 235, |
|
reflectedAngle, |
|
distance = 200, |
|
startPoint, |
|
reflectedPoint; |
|
|
|
var surfaceDragGenerator = d3.drag() |
|
.on("drag", d => { |
|
surfaceAnglePrev = surfaceAngle; |
|
surfaceAngle = geometric.lineAngle([[event.pageX, event.pageY], center]); |
|
surfaceAngleChange = surfaceAngle - surfaceAnglePrev; |
|
update(); |
|
}); |
|
|
|
var surfaceLine = svg.append("line") |
|
.attr("class", "surface-line"); |
|
|
|
var surfaceHandle = svg.append("circle") |
|
.attr("class", "surface-handle") |
|
.attr("r", 10) |
|
.call(surfaceDragGenerator); |
|
|
|
var startDragGenerator = d3.drag() |
|
.on("drag", d => { |
|
startPoint = [event.pageX, event.pageY]; |
|
distance = geometric.lineLength([center, startPoint]); |
|
startAngle = geometric.lineAngle([center, startPoint]); |
|
update(); |
|
}); |
|
|
|
var startLine = svg.append("line") |
|
.attr("class", "start-angle") |
|
.attr("x2", center[0]) |
|
.attr("y2", center[1]); |
|
|
|
var startHandle = svg.append("circle") |
|
.attr("class", "start-handle") |
|
.attr("r", 10) |
|
.call(startDragGenerator) |
|
|
|
var reflectedLine = svg.append("line") |
|
.attr("class", "reflect-angle") |
|
.attr("x2", center[0]) |
|
.attr("y2", center[1]); |
|
|
|
update(); |
|
|
|
function update(){ |
|
surfaceStartPoint = geometric.pointTranslate(center, surfaceAngle, -120); |
|
surfaceEndPoint = geometric.pointTranslate(center, surfaceAngle, 120); |
|
startAngle += surfaceAngleChange; |
|
reflectedAngle = geometric.angleReflect(startAngle - 180, surfaceAngle); // subtract 180 from start angle so that it begins at start point |
|
startPoint = geometric.pointTranslate(center, startAngle, distance); |
|
reflectedPoint = geometric.pointTranslate(center, reflectedAngle, distance); |
|
|
|
surfaceLine |
|
.attr("x1", surfaceStartPoint[0]) |
|
.attr("y1", surfaceStartPoint[1]) |
|
.attr("x2", surfaceEndPoint[0]) |
|
.attr("y2", surfaceEndPoint[1]); |
|
|
|
surfaceHandle |
|
.attr("transform", "translate(" + surfaceStartPoint + ")"); |
|
|
|
startLine |
|
.attr("x1", startPoint[0]) |
|
.attr("y1", startPoint[1]); |
|
|
|
startHandle |
|
.attr("transform", "translate(" + startPoint + ")"); |
|
|
|
reflectedLine |
|
.attr("x1", reflectedPoint[0]) |
|
.attr("y1", reflectedPoint[1]); |
|
} |
|
</script> |
|
|
|
</body> |
|
</html> |