lambdaway
::
verlet
5
|
list
|
login
|
load
|
|
_img https://wikimedia.org/api/rest_v1/media/math/render/svg/8a9087d9d125c94c996b22b906fbc5208e47191e _h1 making a verlet (1|[[2|?view=verlet2]]|[[3|?view=verlet3]]) {canvas {@ id="c"}} {input {@ type="button" value="update" onclick="verlet()"}} _p Le fantastique dans cette affaire c'est que le comportement apparemment élastique de ce système de points simplement liés par des bâtons ne fait intervenir rien d'autre que la force de gravitation de Newton, {b F=mΓ}, qui attire chaque point vers le bas dans un mouvement accéléré, et la contrainte de longueur fixe des bâtons. A chaque étape de la chûte les points se détachent des extrêmités des bâtons et l'algorithme se contente de les remettre aux extrêmités, dans une suite de corrections de position qui finit par remettre tout en ordre, les points aux bouts des bâtons. Le mouvement est si rapide qu'on a du mal à discerner les points détachés et on a l'impression d'un système élastique où les bâtons seraient élastiques. _p Notons aussi que les carrés sont parfois réduits à des triangles. C'est logique, même si on ne s'y attend pas a priori, rien n'interdit à chaque carré de se replier sur sa diagonale. Et il faudrait une diagonale de plus pour le rendre "solide". Voici un algorithme qui va au-delà de ce qu'on imagine au début, en tout cas pour moi, et on peut comprendre en quoi il peut être très utile dans le cas de systèmes plus complexes. Par exemple une passerelle métallique, un pylone de THT, un bras de grue, ... L'algorithme ne connaît rien de la 3D et pourtant il est capable de prévoir le pliage d'un carré suivant sa diagonale. L'algorithme est magique ... et parfaitement logique. Belle collaboration entre la machine et l'homme. _h2 code _p Building dots and sticks {pre °° . 0-1 |/| 3-2 function build(dots, sticks) { dots.push(new Dot(100, 100, (Math.random() - 0.5) * 100)); // 0 dots.push(new Dot(200, 100)); // 1 dots.push(new Dot(200, 200)); // 2 dots.push(new Dot(100, 200)); // 3 sticks.push(new Stick(dots[0], dots[1])) sticks.push(new Stick(dots[1], dots[2])) sticks.push(new Stick(dots[2], dots[3])) sticks.push(new Stick(dots[3], dots[0])) sticks.push(new Stick(dots[3], dots[1])) } 0-1-2 |/|/| 3-4-5 function build(dots, sticks) { dots.push(new Dot(100, 100, (Math.random() - 0.5) * 100)); // 0 dots.push(new Dot(200, 100)); // 1 dots.push(new Dot(300, 100)); // 2 dots.push(new Dot(100, 200)); // 3 dots.push(new Dot(200, 200)); // 4 dots.push(new Dot(300, 200)); // 5 sticks.push(new Stick(dots[0], dots[1])) sticks.push(new Stick(dots[1], dots[2])) sticks.push(new Stick(dots[3], dots[4])) sticks.push(new Stick(dots[4], dots[5])) sticks.push(new Stick(dots[0], dots[3])) sticks.push(new Stick(dots[3], dots[1])) sticks.push(new Stick(dots[1], dots[4])) sticks.push(new Stick(dots[4], dots[2])) sticks.push(new Stick(dots[2], dots[5])) } °°} _ul [[betterprogramming.pub|https://betterprogramming.pub/making-a-verlet-physics-engine-in-javascript-1dff066d7bc5]] _ul [[datagenetics.com|https://datagenetics.com/blog/july22018/index.html]] _ul [[Loup Verlet|https://fr.wikipedia.org/wiki/Intégration_de_Verlet]] | [[itinéraire|https://archive.wikiwix.com/cache/index2.php?url=http%3A%2F%2Fcolblog.blog.lemonde.fr%2F2007%2F04%2F12%2Floup-verlet-physicien-et-philosophe%2F#federation=archive.wikiwix.com&tab=url]] _ul [[fibonacci]] {script function verlet() { function _build(dots, sticks) { dots.push(new Dot(100, 100, (Math.random() - 0.5) * 100)); dots.push(new Dot(200, 100)); dots.push(new Dot(200, 200)); dots.push(new Dot(100, 200)); sticks.push(new Stick(dots[0], dots[1])) sticks.push(new Stick(dots[1], dots[2])) sticks.push(new Stick(dots[2], dots[3])) sticks.push(new Stick(dots[3], dots[0])) sticks.push(new Stick(dots[3], dots[1])) } function build(dots, sticks) { dots.push(new Dot(100, 100, (Math.random() - 0.5) * 100)); // 0 dots.push(new Dot(200, 100)); // 1 dots.push(new Dot(300, 100)); // 2 dots.push(new Dot(100, 200)); // 3 dots.push(new Dot(200, 200)); // 4 dots.push(new Dot(300, 200)); // 5 sticks.push(new Stick(dots[0], dots[1])) sticks.push(new Stick(dots[1], dots[2])) sticks.push(new Stick(dots[3], dots[4])) sticks.push(new Stick(dots[4], dots[5])) sticks.push(new Stick(dots[0], dots[3])) sticks.push(new Stick(dots[3], dots[1])) sticks.push(new Stick(dots[1], dots[4])) sticks.push(new Stick(dots[4], dots[2])) sticks.push(new Stick(dots[2], dots[5])) } class Dot { constructor(x, y, vx, vy) { this.pos = new Vector(x, y); this.oldpos = new Vector(x + (vx||0), y + (vy||0)); // velocity x, y this.friction = 0.97; this.groundFriction = 0.7; this.gravity = new Vector(0, 1); this.radius = 5; this.color = "#e62a4f"; this.mass = 1; } update() { let vel = Vector.sub(this.pos, this.oldpos); vel.mult(this.friction); // if the point touches the ground set groundFriction if (this.pos.y >= CANVAS_HEIGHT - this.radius && vel.magSq() > 0.000001) { var m = vel.mag(); vel.x /= m; vel.y /= m; vel.mult(m * this.groundFriction); } this.oldpos.setXY(this.pos.x, this.pos.y); this.pos.add(vel); this.pos.add(this.gravity); } constrain() { if (this.pos.x > CANVAS_WIDTH - this.radius) { this.pos.x = CANVAS_WIDTH - this.radius; } if (this.pos.x < this.radius) { this.pos.x = this.radius; } if (this.pos.y > CANVAS_HEIGHT - this.radius) { this.pos.y = CANVAS_HEIGHT - this.radius; } if (this.pos.y < this.radius) { this.pos.y = this.radius; } }; render(ctx) { ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2); ctx.fill(); ctx.closePath(); } } class Stick { constructor(p1, p2, length) { this.startPoint = p1; this.endPoint = p2; this.stiffness = 2; this.color = '#f5476a'; // if the length is not given then calculate the distance based on position if (!length) { this.length = this.startPoint.pos.dist(this.endPoint.pos); } else { this.length = length; } } update() { // calculate the distance between two dots let dx = this.endPoint.pos.x - this.startPoint.pos.x; let dy = this.endPoint.pos.y - this.startPoint.pos.y; // pythagoras theorem let dist = Math.sqrt(dx * dx + dy * dy); // calculate the resting distance betwen the dots let diff = (this.length - dist) / dist * this.stiffness; // getting the offset of the points let offsetx = dx * diff * 0.5; let offsety = dy * diff * 0.5; // calculate mass let m1 = this.startPoint.mass + this.endPoint.mass; let m2 = this.startPoint.mass / m1; m1 = this.endPoint.mass / m1; // and finally apply the offset with calculated mass if (!this.startPoint.pinned) { this.startPoint.pos.x -= offsetx * m1; this.startPoint.pos.y -= offsety * m1; } if (!this.endPoint.pinned) { this.endPoint.pos.x += offsetx * m2; this.endPoint.pos.y += offsety * m2; } } render(ctx) { ctx.beginPath(); ctx.strokeStyle = this.color; ctx.moveTo(this.startPoint.pos.x, this.startPoint.pos.y); ctx.lineTo(this.endPoint.pos.x, this.endPoint.pos.y); ctx.stroke(); ctx.closePath(); } } function animate() { ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); for (let d of dots) { d.update(); d.constrain(); d.render(ctx); } for (let s of sticks) { s.update(); s.render(ctx); } requestAnimationFrame(animate); } let canvas = document.getElementById("c"); let ctx = canvas.getContext("2d"); let CANVAS_WIDTH = 580; // window.innerWidth-5; let CANVAS_HEIGHT = 580; // window.innerHeight-5; canvas.width = CANVAS_WIDTH; canvas.height = CANVAS_HEIGHT; let dots = []; let sticks = []; build( dots, sticks ); animate(); } // end verlet setTimeout( verlet, 1 ); setInterval( verlet, 3000 ); } ;; un peu d'algebre vectorielle {script /** * Vector.js v1.0.0 * @author Anurag Hazra * @borrows p5.Vector * @param {number} x * @param {number} y */ function Vector(x, y) { this.x = x || 0; this.y = y || 0; } // Static Functions Vector.dist = function (v1, v2) { return v1.dist(v2); } Vector.distSq = function (v1, v2) { return v1.distSq(v2); } Vector.sub = function (v1, v2) { return new Vector(v1.x - v2.x, v1.y - v2.y); }; Vector.add = function (v1, v2) { return new Vector(v1.x + v2.x, v1.y + v2.y); }; Vector.fromAngle = function (angle) { let v = new Vector(0, 0); v.x = Math.cos(angle); v.y = Math.sin(angle); return v; } Vector.random2D = function (v) { return Vector.fromAngle(Math.random() * Math.PI * 180); } Vector.prototype = { add: function (x, y) { if (arguments.length === 1) { this.x += x.x; this.y += x.y; } else if (arguments.length === 2) { this.x += x; this.y += y; } return this; }, sub: function (x, y) { if (arguments.length === 1) { this.x -= x.x; this.y -= x.y; } else if (arguments.length === 2) { this.x -= x; this.y -= y; } return this; }, mult: function (v) { if (typeof v === 'number') { this.x *= v; this.y *= v; } else { this.x *= v.x; this.y *= v.y; } return this; }, div: function (v) { if (typeof v === 'number') { this.x /= v; this.y /= v; } else { this.x /= v.x; this.y /= v.y; } return this; }, setAngle: function(angle) { var len = this.mag(); this.x = Math.cos(angle) * len; this.y = Math.sin(angle) * len; }, mag: function () { return Math.sqrt(this.x * this.x + this.y * this.y); }, magSq: function () { return (this.x * this.x + this.y * this.y); }, setXY : function (x, y) { this.x = x; this.y = y; return this; }, setMag: function (value) { this.normalize(); this.mult(value); return this; }, normalize: function () { let m = this.mag(); if (m > 0) { this.div(m); } return this; }, limit: function (max) { if (this.mag() > max) { this.normalize(); this.mult(max); } return this; }, heading: function () { return (-Math.atan2(-this.y, this.x)); }, dist: function (v) { let dx = this.x - v.x; let dy = this.y - v.y; return Math.sqrt(dx * dx + dy * dy); }, distSq: function (v) { let dx = this.x - v.x; let dy = this.y - v.y; return (dx * dx + dy * dy); }, copy: function () { return new Vector(this.x, this.y); }, negative: function () { this.x = -this.x; this.y = -this.y; return this; }, array: function () { return [this.x, this.y]; }, toString: function () { return "[" + this.x + ", " + this.y + ", " + this.z + "]"; }, project : function (v) { var coeff = ((this.x * v.x) + (this.y * v.y)) / ((v.x * v.x) + (v.y * v.y)); this.x = coeff * v.x; this.y = coeff * v.y; return this; }, rotate : function (a) { var b = this.heading() + a; var c = this.mag(); this.x = Math.cos(b) * c; this.y = Math.sin(b) * c; } } } {style #c { background:#eee; box-shadow:0 0 8px #000; } }
lambdaway v.20211111