Created
July 25, 2019 01:02
-
-
Save skyzh/cf663f4adc0e2bffb6b15415e7d554e2 to your computer and use it in GitHub Desktop.
Use d3.js to visualize collision between point and segment
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Physics Test</title> | |
</head> | |
<body> | |
<svg id="main" width="800" height="800"></svg> | |
<script src="https://d3js.org/d3.v5.min.js"></script> | |
<script src="main.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const svg = d3.select('#main') | |
const lines = [ | |
[10, 10, 10, 790], | |
[10, 10, 790, 10], | |
[790, 790, 10, 790], | |
[790, 790, 790, 10]] | |
const balls = [] | |
const BALL_RADIUS = 5 | |
const WIDTH = 800 | |
const HEIGHT = 800 | |
function generate_balls() { | |
for (let i = 0; i < 30; i++) { | |
balls.push([ | |
Math.random() * WIDTH, | |
Math.random() * HEIGHT, | |
Math.random() * BALL_RADIUS + 2, | |
Math.random() * BALL_RADIUS + 2]) | |
} | |
for (let i = 0; i < 10; i++) { | |
const x1 = Math.random() * WIDTH | |
const y1 = Math.random() * HEIGHT | |
lines.push([ | |
x1, | |
y1, | |
x1 + Math.random() * 100, | |
y1 + Math.random() * 100 | |
]) | |
} | |
} | |
generate_balls() | |
function update() { | |
svg.selectAll('.block') | |
.data(lines) | |
.attr('x1', d => d[0]) | |
.attr('y1', d => d[1]) | |
.attr('x2', d => d[2]) | |
.attr('y2', d => d[3]) | |
.enter() | |
.append('line') | |
.attr('class', 'block') | |
.attr('stroke', '#000') | |
.attr('stroke-width', '1') | |
.attr('stroke-linecap', 'round') | |
.exit() | |
.remove() | |
svg.selectAll('.ball') | |
.data(balls) | |
.attr('cx', d => d[0]) | |
.attr('cy', d => d[1]) | |
.attr('r', BALL_RADIUS) | |
.enter() | |
.append('circle') | |
.attr('class', 'ball') | |
.attr('stroke', '#000') | |
.exit() | |
.remove() | |
svg.selectAll('.velocity') | |
.data(balls) | |
.attr('x1', d => d[0]) | |
.attr('y1', d => d[1]) | |
.attr('x2', d => d[0] + d[2] * BALL_RADIUS) | |
.attr('y2', d => d[1] + d[3] * BALL_RADIUS) | |
.enter() | |
.append('line') | |
.attr('class', 'velocity') | |
.attr('stroke', '#f00') | |
.exit() | |
.remove() | |
} | |
function vec_d(line) { | |
return [line[2] - line[0], line[3] - line[1]] | |
} | |
function vec_len(vec) { | |
return Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]) | |
} | |
function vec_unit(vec) { | |
const length = vec_len(vec) | |
if (length == 0) return [0, 0] | |
return [vec[0] / length, vec[1] / length] | |
} | |
function vec_n(unit_d) { | |
return [-unit_d[1], unit_d[0]] | |
} | |
function vec_dot(vec1, vec2) { | |
return vec1[0] * vec2[0] + vec1[1] * vec2[1] | |
} | |
function distance_to(point, line) { | |
const vec_p = [line[0] - point[0], line[1] - point[1]] | |
return Math.abs(vec_dot(vec_p, vec_n(vec_unit(vec_d(line))))) | |
} | |
function in_range(point, line) { | |
const x1 = Math.min(line[0], line[2]) | |
const x2 = Math.max(line[0], line[2]) | |
const y1 = Math.min(line[1], line[3]) | |
const y2 = Math.max(line[1], line[3]) | |
return x1 <= point[0] && point[0] <= x2 || y1 <= point[1] && point[1] <= y2 | |
} | |
function delta(point, line) { | |
const [x, y] = point | |
const [x1, y1, x2, y2] = line | |
return (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1) | |
} | |
function run() { | |
update() | |
balls.forEach(ball => { | |
const [px, py] = [ball[0], ball[1]] | |
let [vx, vy] = [ball[2], ball[3]] | |
vy += 0.1 | |
lines.forEach(line => { | |
const ball_pos = [px, py] | |
const [nx, ny] = [px + vx, py + vy] | |
if (in_range(ball_pos, line)) { | |
if (delta([px, py], line) * delta([nx, ny], line) < 0) { | |
const u_n = vec_unit(vec_n(vec_d(line))) | |
const v_proj = vec_dot(u_n, [vx, vy]) | |
vx -= 2 * u_n[0] * v_proj | |
vy -= 2 * u_n[1] * v_proj | |
} | |
} | |
}) | |
ball[0] = px + vx | |
ball[1] = py + vy | |
ball[2] = vx | |
ball[3] = vy | |
}) | |
window.requestAnimationFrame(run) | |
} | |
run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment