Move SVG names to HTML, fix sign error in collide, move objectsCollide to its own function

This commit is contained in:
Anthony Wang 2024-01-29 11:13:02 -05:00
parent d2ecf019b3
commit 64e928da30
Signed by: a
SSH key fingerprint: SHA256:B5ADfMCqd2M7d/jtXDoihAV/yfXOAbWWri9+GdCN4hQ
2 changed files with 66 additions and 60 deletions

View file

@ -1,11 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>dumb physics engine</title>
<meta name="description" content="Converts SVGs into a list of circles and use those circles for dumb collision checking">
<title>Dumb physics engine</title>
<meta name="description" content="Converts SVGs into a set of circles and use those circles for dumb collision checking">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!--
Draw 200x200 SVGs in Inkscape with the pencil tool and a stroke width of 20
Make sure the scale is set to 1 and that the id is set to a unique value
Also, change the stroke to currentColor so we can style the color with CSS
-->
<svg id="hiragana-ni"></svg>
<svg id="hiragana-small-ya"></svg>
<svg id="hiragana-a"></svg>
<script src="script.js"></script>
</body>
</html>

114
script.js
View file

@ -1,29 +1,22 @@
// Inject SVG into DOM synchronously because we can't access the DOM of SVGs inside img tags
function injectSVG(svg) {
document.querySelectorAll("svg").forEach(function(svg) {
let req = new XMLHttpRequest()
req.open("GET", svg, false)
req.open("GET", svg.id + ".svg", false)
req.send()
document.body.innerHTML += req.responseText
}
svg.outerHTML = req.responseText
})
// Draw 200x200 SVGs in Inkscape with the pencil tool and a stroke width of 20
// Make sure the scale is set to 1 and that the id is set to a unique value
// Also, change the stroke to currentColor so we can style the color with CSS
injectSVG("hiragana-ni.svg")
injectSVG("hiragana-small-ya.svg")
injectSVG("hiragana-a.svg")
let rad = 10
let size = 200
let A = []
let rad = 10 // Stroke width
let size = 200 // SVG width and height
let A = [] // Objects
let cnt = 0
document.querySelectorAll("svg").forEach(function(svg) {
// Move objects so they aren't overlapping
svg.style.left = 1.5 * size * cnt++ + "px"
svg.style.top = "50px"
let a = {
id: svg.id, // Unique ID
p: [], // Collision circles of Array instances creates a new array populated with the results of calling a provided function on every element in the calling array. Try it Syntax js map(callbackFn) map(callbackFn, thisArg) Parameters callbackFn A function to execute for each element in the array.
p: [], // Collision circles
cm: svg.createSVGPoint(), // Center of mass
vx: Math.random(), // x velocity
vy: Math.random(), // y velocity
@ -32,17 +25,17 @@ document.querySelectorAll("svg").forEach(function(svg) {
}
svg.querySelectorAll("path").forEach(function(path) {
// Get circles on path for collision checking
for (let i = 0; i < path.getTotalLength(); i += rad) {
for (let i = 0; i < path.getTotalLength(); i += 5) {
const p = path.getPointAtLength(i)
a.cm.x += p.x
a.cm.y += p.y
a.p.push(p)
// Show circles for debugging
/* let circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", p.x);
circle.setAttribute("cy", p.y);
circle.setAttribute("r", rad);
circle.setAttribute("fill", "red");
/* let circle = document.createElementNS("http://www.w3.org/2000/svg", "circle")
circle.setAttribute("cx", p.x)
circle.setAttribute("cy", p.y)
circle.setAttribute("r", rad)
circle.setAttribute("fill", "red")
svg.appendChild(circle) */
}
})
@ -87,6 +80,7 @@ function collide(a, b, c, n) {
// https://physics.stackexchange.com/questions/686640/resolving-angular-components-in-2d-circular-rigid-body-collision-response
// I still don't know how to derive this magic but I'm convinced it works
// No idea if there's a sign error
// It looks fine though
const ca = {x: a.x - c.x, y: a.y - c.y}
const cb = {x: b.x - c.x, y: b.y - c.y}
const v = n.x * (a.vx - b.vx) + n.y * (a.vy - b.vy) - a.w * cr(ca, n) + b.w * cr(cb, n)
@ -97,8 +91,8 @@ function collide(a, b, c, n) {
a.w += cr(ca, n) * j / a.mi
b.vx += n.x * j / b.m
b.vy += n.y * j / b.m
b.w += cr(cb, n) * j / b.mi
console.log('hi')
b.w += -cr(cb, n) * j / b.mi
console.log('boop')
}
// Collision of object a with wall at position k and direction d
@ -123,6 +117,41 @@ function wallCollide(a, k, d) {
}
}
// Collision of object a with object b
function objectsCollide(a, b) {
if (ds(a, b) < size * size) {
// Objects are close
let c = {x: 0, y: 0, cnt: 0}
let n = {x: 0, y: 0}
for (const p of a.p.map(x => rot(a, x))) {
// p is close to object b
if (ds(p, b) < size * size) {
for (const q of b.p.map(x => rot(b, x))) {
const d = ds(p, q)
if (d < 4 * rad * rad) {
// Collision!
// These calculations are a bit sketchy but I guess they work?
c.x += p.x + q.x
c.y += p.y + q.y
c.cnt++
n.x += (p.x - q.x) / d
n.y += (p.y - q.y) / d
}
}
}
}
if (c.cnt > 0) {
c.x /= 2 * c.cnt
c.y /= 2 * c.cnt
// Normalize n
let norm = Math.sqrt(n.x ** 2 + n.y ** 2)
n.x /= norm
n.y /= norm
collide(a, b, c, n)
}
}
}
function tick() {
// Move each object one step
for (let a of A) {
@ -145,38 +174,7 @@ function tick() {
// Check collisions between objects
for (let i = 0; i < A.length; i++) {
for (let j = i + 1; j < A.length; j++) {
let a = A[i]
let b = A[j]
if (ds(a, b) < size * size) {
// Objects are close
let c = {x: 0, y: 0, cnt: 0}
let n = {x: 0, y: 0}
for (const p of a.p.map(x => rot(a, x))) {
// p is close to object b
if (ds(p, b) < size * size) {
for (const q of b.p.map(x => rot(b, x))) {
const d = ds(p, q)
if (d < 4 * rad * rad) {
// Collision!
c.x += p.x + q.x
c.y += p.y + q.y
c.cnt++
n.x += (p.x - q.x) / d
n.y += (p.y - q.y) / d
}
}
}
}
if (c.cnt > 0) {
c.x /= 2 * c.cnt
c.y /= 2 * c.cnt
let norm = Math.sqrt(n.x ** 2 + n.y ** 2)
n.x /= norm
n.y /= norm
console.log(c.cnt, c, n)
collide(a, b, c, n)
}
}
objectsCollide(A[i], A[j])
}
}
@ -211,8 +209,8 @@ function updatev(event) {
circle.style.transform = "scale(500)"
circle.style.opacity = "0"
setTimeout(function () {
document.body.removeChild(circle);
}, 1000);
document.body.removeChild(circle)
}, 1000)
}
cnt = 0