diff --git a/index.html b/index.html index 822899d..eb6514b 100644 --- a/index.html +++ b/index.html @@ -1,11 +1,19 @@ - dumb physics engine - + Dumb physics engine + + + + + diff --git a/script.js b/script.js index 188dbe3..d40711a 100644 --- a/script.js +++ b/script.js @@ -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