Javascript小球
<html> <head> <script type="text/javascript"> function log(msg) { var textArea; textArea = document.getElementById("logArea"); textArea.value = msg + "\n" + textArea.value; } function clearLog() { var textArea; textArea = document.getElementById("logArea"); textArea.value = ""; } function Vector(x, y) { this.x = x; this.y = y; this.equal = function(v) { return this.x == v.getX() && this.y == v.getY(); } this.getX = function() { return this.x; } this.getY = function() { return this.y; } this.setX = function(x) { this.x = x; } this.setY = function(y) { this.y = y; } this.addX = function(x) { this.x += x; } this.addY = function(y) { this.y += y; } this.set = function(v) { this.x = v.getX(); this.y = v.getY(); } this.add = function(v) { this.x += v.getX(); this.y += v.getY(); } this.sub = function(v) { this.x -= v.getX(); this.y -= v.getY(); } this.dotProd = function(v) { return this.x * v.getX() + this.y * v.getY(); } this.length = function() { return Math.sqrt(this.x * this.x + this.y * this.y); } this.scale = function(scaleFactor) { this.x *= scaleFactor; this.y *= scaleFactor; } this.toString = function() { return " X: " + this.x + " Y: " + this.y; } } function Environment(x, y, w, h) { this.left = x; this.right = x + w; this.top = y; this.buttom = y + h; this.r = new Vector(0.0, 0.0); this.collision = function(curPos, prevPos) { var collide = false; var i; if(curPos.getX() < this.left) { curPos.setX(this.left); collide = true; } else if(curPos.getX() > this.right) { curPos.setX(this.right); collide = true; } if(curPos.getY() < this.top) { curPos.setY(this.top); collide = true; } else if(curPos.getY() > this.buttom) { curPos.setY(this.buttom); collide = true; } return collide; } this.draw = function(ctx, scaleFactor) { } } function PointMass(cx, cy, mass) { this.cur = new Vector(cx, cy); this.prev = new Vector(cx, cy); this.mass = mass; this.force = new Vector(0.0, 0.0); this.result = new Vector(0.0, 0.0); this.friction = 0.01; this.getXPos = function() { return this.cur.getX(); } this.getYPos = function() { return this.cur.getY(); } this.getPos = function() { return this.cur; } this.getXPrevPos = function() { return this.prev.getX(); } this.getYPrevPos = function() { return this.prev.getY(); } this.getPrevPos = function() { return this.prev; } this.addXPos = function(dx) { this.cur.addX(dx); } this.addYPos = function(dy) { this.cur.addY(dy); } this.setForce = function(force) { this.force.set(force); } this.addForce = function(force) { this.force.add(force); } this.getMass = function() { return this.mass; } this.setMass = function(mass) { this.mass = mass; } this.move = function(dt) { var t, a, c, dtdt; dtdt = dt * dt; a = this.force.getX() / this.mass; c = this.cur.getX(); t = (2.0 - this.friction) * c - (1.0 - this.friction) * this.prev.getX() + a * dtdt; this.prev.setX(c); this.cur.setX(t); a = this.force.getY() / this.mass; c = this.cur.getY(); t = (2.0 - this.friction) * c - (1.0 - this.friction) * this.prev.getY() + a * dtdt; this.prev.setY(c); this.cur.setY(t); } this.setFriction = function(friction) { this.friction = friction; } this.getVelocity = function() { var cXpX, cYpY; cXpX = this.cur.getX() - this.prev.getX(); cYpY = this.cur.getY() - this.prev.getY(); return cXpX * cXpX + cYpY * cYpY; } this.draw = function(ctx, scaleFactor) { ctx.lineWidth = 2; ctx.fillStyle = '#000000'; ctx.strokeStyle = '#000000'; ctx.beginPath(); ctx.arc(this.cur.getX() * scaleFactor, this.cur.getY() * scaleFactor, 4.0, 0.0, Math.PI * 2.0, true); ctx.fill(); } } function ConstraintY(pointMass, y, shortConst, longConst) { this.pointMass = pointMass; this.y = y; this.delta = new Vector(0.0, 0.0); this.shortConst = shortConst; this.longConst = longConst; this.sc = function() { var dist; dist = Math.abs(this.pointMass.getYPos() - this.y); this.delta.setY(-dist); if(this.shortConst != 0.0 && dist < this.shortConst) { var scaleFactor; scaleFactor = this.shortConst / dist; this.delta.scale(scaleFactor); pointMass.getPos().sub(this.delta); } else if(this.longConst != 0.0 && dist > this.longConst) { var scaleFactor; scaleFactor = this.longConst / dist; this.delta.scale(scaleFactor); pointMass.getPos().sub(this.delta); } } } function Joint(pointMassA, pointMassB, shortConst, longConst) { this.pointMassA = pointMassA; this.pointMassB = pointMassB; this.delta = new Vector(0.0, 0.0); this.pointMassAPos = pointMassA.getPos(); this.pointMassBPos = pointMassB.getPos(); this.delta.set(this.pointMassBPos); this.delta.sub(this.pointMassAPos); this.shortConst = this.delta.length() * shortConst; this.longConst = this.delta.length() * longConst; this.scSquared = this.shortConst * this.shortConst; this.lcSquared = this.longConst * this.longConst; this.setDist = function(shortConst, longConst) { this.shortConst = shortConst; this.longConst = longConst; this.scSquared = this.shortConst * this.shortConst; this.lcSquared = this.longConst * this.longConst; } this.scale = function(scaleFactor) { this.shortConst = this.shortConst * scaleFactor; this.longConst = this.longConst * scaleFactor; this.scSquared = this.shortConst * this.shortConst; this.lcSquared = this.longConst * this.longConst; } this.sc = function() { this.delta.set(this.pointMassBPos); this.delta.sub(this.pointMassAPos); var dp = this.delta.dotProd(this.delta); if(this.shortConst != 0.0 && dp < this.scSquared) { var scaleFactor; scaleFactor = this.scSquared / (dp + this.scSquared) - 0.5; this.delta.scale(scaleFactor); this.pointMassAPos.sub(this.delta); this.pointMassBPos.add(this.delta); } else if(this.longConst != 0.0 && dp > this.lcSquared) { var scaleFactor; scaleFactor = this.lcSquared / (dp + this.lcSquared) - 0.5; this.delta.scale(scaleFactor); this.pointMassAPos.sub(this.delta); this.pointMassBPos.add(this.delta); } } } function Stick(pointMassA, pointMassB) { function pointMassDist(pointMassA, pointMassB) { var aXbX, aYbY; aXbX = pointMassA.getXPos() - pointMassB.getXPos(); aYbY = pointMassA.getYPos() - pointMassB.getYPos(); return Math.sqrt(aXbX * aXbX + aYbY * aYbY); } this.length = pointMassDist(pointMassA, pointMassB); this.lengthSquared = this.length * this.length; this.pointMassA = pointMassA; this.pointMassB = pointMassB; this.delta = new Vector(0.0, 0.0); this.getPointMassA = function() { return this.pointMassA; } this.getPointMassB = function() { return this.pointMassB; } this.scale = function(scaleFactor) { this.length *= scaleFactor; this.lengthSquared = this.length * this.length; } this.sc = function(env) { var dotProd, scaleFactor; var pointMassAPos, pointMassBPos; pointMassAPos = this.pointMassA.getPos(); pointMassBPos = this.pointMassB.getPos(); this.delta.set(pointMassBPos); this.delta.sub(pointMassAPos); dotProd = this.delta.dotProd(this.delta); scaleFactor = this.lengthSquared / (dotProd + this.lengthSquared) - 0.5; this.delta.scale(scaleFactor); pointMassAPos.sub(this.delta); pointMassBPos.add(this.delta); } this.setForce = function(force) { this.pointMassA.setForce(force); this.pointMassB.setForce(force); } this.addForce = function(force) { this.pointMassA.addForce(force); this.pointMassB.addForce(force); } this.move = function(dt) { this.pointMassA.move(dt); this.pointMassB.move(dt); } this.draw = function(ctx, scaleFactor) { this.pointMassA.draw(ctx, scaleFactor); this.pointMassB.draw(ctx, scaleFactor); ctx.lineWidth = 3; ctx.fillStyle = '#000000'; ctx.strokeStyle = '#000000'; ctx.beginPath(); ctx.moveTo(this.pointMassA.getXPos() * scaleFactor, this.pointMassA.getYPos() * scaleFactor); ctx.lineTo(this.pointMassB.getXPos() * scaleFactor, this.pointMassB.getYPos() * scaleFactor); ctx.stroke(); } } function Spring(restLength, stiffness, damper, pointMassA, pointMassB) { this.restLength = restLength; this.stiffness = stiffness; this.damper = damper; this.pointMassA = pointMassA; this.pointMassB = pointMassB; this.tmp = Vector(0.0, 0.0); this.sc = function(env) { env.collistion(this.pointMassA.getPos(), this.pointMassA.getPrevPos()); env.collistion(this.pointMassB.getPos(), this.pointMassB.getPrevPos()); } this.move = function(dt) { var aXbX; var aYbY; var springForce; var length; aXbX = this.pointMassA.getXPos() - this.pointMassB.getXPos(); aYbY = this.pointMassA.getYPos() - this.pointMassB.getYPos(); length = Math.sqrt(aXbX * aXbX + aYbY * aYbY); springForce = this.stiffness * (length / this.restLength - 1.0); var avXbvX; var avYbvY; var damperForce; avXbvX = this.pointMassA.getXVel() - this.pointMassB.getXVel(); avYbvY = this.pointMassA.getYVel() - this.pointMassB.getYVel(); damperForce = avXbvX * aXbX + avYbvY * aYbY; damperForce *= this.damper; var fx; var fy; fx = (springForce + damperForce) * aXbX; fy = (springForce + damperForce) * aYbY; this.tmp.setX(-fx); this.tmp.setY(-ft); this.pointMassA.addForce(this.tmp); this.tmp.setX(fx); this.tmp.setY(ft); this.pointMassB.addForce(this.tmp); this.pointMassA.move(dt); this.pointMassB.move(dt); } this.addForce = function(force) { this.pointMassA.addForce(force); this.pointMassB.addForce(force); } this.draw = function(ctx, scaleFactor) { this.pointMassA.draw(ctx, scaleFactor); this.pointMassB.draw(ctx, scaleFactor); ctx.fillStyle = '#000000'; ctx.strokeStyle = '#000000'; ctx.beginPath(); ctx.moveTo(this.pointMassA.getXPos() * scaleFactor, this.pointMassA.getYPos() * scaleFactor); ctx.lineTo(this.pointMassB.getXPos() * scaleFactor, this.pointMassB.getXPos() * scaleFactor); ctx.stroke(); } } function Blob(x, y, radius, numPointMasses) { this.x = x; this.y = y; this.sticks = new Array(); this.pointMasses = new Array(); this.joints = new Array(); this.middlePointMass; this.radius = radius; this.drawFaceStyle = 1; this.drawEyeStyle = 1; this.selected = false; numPointMasses = 8; var f = 0.1; var low = 0.95, high = 1.05; var t, i, p; function clampIndex(index, maxIndex) { index += maxIndex; return index % maxIndex; } for(i = 0, t = 0.0; i < numPointMasses; i++) { this.pointMasses[i] = new PointMass(Math.cos(t) * radius + x, Math.sin(t) * radius + y, 1.0); t += 2.0 * Math.PI / numPointMasses; } this.middlePointMass = new PointMass(x, y, 1.0); this.pointMasses[0].setMass(4.0); this.pointMasses[1].setMass(4.0); for(i = 0; i < numPointMasses; i++) { this.sticks[i] = new Stick(this.pointMasses[i], this.pointMasses[clampIndex(i + 1, numPointMasses)]); } for(i = 0, p = 0; i < numPointMasses; i++) { this.joints[p++] = new Joint(this.pointMasses[i], this.pointMasses[clampIndex(i + numPointMasses / 2 + 1, numPointMasses)], low, high); this.joints[p++] = new Joint(this.pointMasses[i], this.middlePointMass, high * 0.9, low * 1.1); // 0.8, 1.2 works } this.addBlob = function(blob) { var index = this.joints.length; var dist; this.joints[index] = new Joint(this.middlePointMass, blob.getMiddlePointMass(), 0.0, 0.0); dist = this.radius + blob.getRadius(); this.joints[index].setDist(dist * 0.95, 0.0); } this.getMiddlePointMass = function() { return this.middlePointMass; } this.getRadius = function() { return this.radius; } this.getXPos = function() { return this.middlePointMass.getXPos(); } this.getYPos = function() { return this.middlePointMass.getYPos(); } this.scale = function(scaleFactor) { var i; for(i = 0; i < this.joints.length; i++) { this.joints[i].scale(scaleFactor); } for(i = 0; i < this.sticks.length; i++) { this.sticks[i].scale(scaleFactor); } this.radius *= scaleFactor; } this.move = function(dt) { var i; for(i = 0; i < this.pointMasses.length; i++) { this.pointMasses[i].move(dt); } this.middlePointMass.move(dt); } this.sc = function(env) { var i, j; for(j = 0; j < 4; j++) { for(i = 0; i < this.pointMasses.length; i++) { if(env.collision(this.pointMasses[i].getPos(), this.pointMasses[i].getPrevPos()) == true) { this.pointMasses[i].setFriction(0.75); } else { this.pointMasses[i].setFriction(0.01); } } for(i = 0; i < this.sticks.length; i++) { this.sticks[i].sc(env); } for(i = 0; i < this.joints.length; i++) { this.joints[i].sc(); } } } this.setForce = function(force) { var i; for(i = 0; i < this.pointMasses.length; i++) { this.pointMasses[i].setForce(force); } this.middlePointMass.setForce(force); } this.addForce = function(force) { var i; for(i = 0; i < this.pointMasses.length; i++) { this.pointMasses[i].addForce(force); } this.middlePointMass.addForce(force); this.pointMasses[0].addForce(force); this.pointMasses[0].addForce(force); this.pointMasses[0].addForce(force); this.pointMasses[0].addForce(force); } this.moveTo = function(x, y) { var i, blobPos; blobPos = this.middlePointMass.getPos(); x -= blobPos.getX(x); y -= blobPos.getY(y); for(i = 0; i < this.pointMasses.length; i++) { blobPos = this.pointMasses[i].getPos(); blobPos.addX(x); blobPos.addY(y); } blobPos = this.middlePointMass.getPos(); blobPos.addX(x); blobPos.addY(y); } this.setSelected = function(selected) { this.selected = selected; } this.drawEars = function(ctx, scaleFactor) { ctx.strokeStyle = "#000000"; ctx.fillStyle = "#FFFFFF"; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo((-0.55 * this.radius) * scaleFactor, (-0.35 * this.radius) * scaleFactor); ctx.lineTo((-0.52 * this.radius) * scaleFactor, (-0.55 * this.radius) * scaleFactor); ctx.lineTo((-0.45 * this.radius) * scaleFactor, (-0.40 * this.radius) * scaleFactor); ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.moveTo((0.55 * this.radius) * scaleFactor, (-0.35 * this.radius) * scaleFactor); ctx.lineTo((0.52 * this.radius) * scaleFactor, (-0.55 * this.radius) * scaleFactor); ctx.lineTo((0.45 * this.radius) * scaleFactor, (-0.40 * this.radius) * scaleFactor); ctx.fill(); ctx.stroke(); } this.drawHappyEyes1 = function(ctx, scaleFactor) { ctx.lineWidth = 1; ctx.fillStyle = "#FFFFFF"; ctx.beginPath(); ctx.arc((-0.15 * this.radius) * scaleFactor, (-0.20 * this.radius) * scaleFactor, this.radius * 0.12 * scaleFactor, 0, 2.0 * Math.PI, false); ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.arc(( 0.15 * this.radius) * scaleFactor, (-0.20 * this.radius) * scaleFactor, this.radius * 0.12 * scaleFactor, 0, 2.0 * Math.PI, false); ctx.fill(); ctx.stroke(); ctx.fillStyle = "#000000"; ctx.beginPath(); ctx.arc((-0.15 * this.radius) * scaleFactor, (-0.17 * this.radius) * scaleFactor, this.radius * 0.06 * scaleFactor, 0, 2.0 * Math.PI, false); ctx.fill(); ctx.beginPath(); ctx.arc(( 0.15 * this.radius) * scaleFactor, (-0.17 * this.radius) * scaleFactor, this.radius * 0.06 * scaleFactor, 0, 2.0 * Math.PI, false); ctx.fill(); } this.drawHappyEyes2 = function(ctx, scaleFactor) { ctx.lineWidth = 1; ctx.fillStyle = "#FFFFFF"; ctx.beginPath(); ctx.arc((-0.15 * this.radius) * scaleFactor, (-0.20 * this.radius) * scaleFactor, this.radius * 0.12 * scaleFactor, 0, 2.0 * Math.PI, false); ctx.stroke(); ctx.beginPath(); ctx.arc(( 0.15 * this.radius) * scaleFactor, (-0.20 * this.radius) * scaleFactor, this.radius * 0.12 * scaleFactor, 0, 2.0 * Math.PI, false); ctx.stroke(); ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo((-0.25 * this.radius) * scaleFactor, (-0.20 * this.radius) * scaleFactor); ctx.lineTo((-0.05 * this.radius) * scaleFactor, (-0.20 * this.radius) * scaleFactor); ctx.stroke(); ctx.beginPath(); ctx.moveTo(( 0.25 * this.radius) * scaleFactor, (-0.20 * this.radius) * scaleFactor); ctx.lineTo(( 0.05 * this.radius) * scaleFactor, (-0.20 * this.radius) * scaleFactor); ctx.stroke(); } this.drawHappyFace1 = function(ctx, scaleFactor) { ctx.lineWidth = 2; ctx.strokeStyle = "#000000"; ctx.fillStyle = "#000000"; ctx.beginPath(); ctx.arc(0.0, 0.0, this.radius * 0.25 * scaleFactor, 0, Math.PI, false); ctx.stroke(); } this.drawHappyFace2 = function(ctx, scaleFactor) { ctx.lineWidth = 2; ctx.strokeStyle = "#000000"; ctx.fillStyle = "#000000"; ctx.beginPath(); ctx.arc(0.0, 0.0, this.radius * 0.25 * scaleFactor, 0, Math.PI, false); ctx.fill(); } this.drawOohFace = function(ctx, scaleFactor) { ctx.lineWidth = 2; ctx.strokeStyle = "#000000"; ctx.fillStyle = "#000000"; ctx.beginPath(); ctx.arc(0.0, (0.1 * this.radius) * scaleFactor, this.radius * 0.25 * scaleFactor, 0, Math.PI, false); ctx.fill(); ctx.beginPath(); ctx.moveTo((-0.25 * this.radius) * scaleFactor, (-0.3 * this.radius) * scaleFactor); ctx.lineTo((-0.05 * this.radius) * scaleFactor, (-0.2 * this.radius) * scaleFactor); ctx.lineTo((-0.25 * this.radius) * scaleFactor, (-0.1 * this.radius) * scaleFactor); ctx.moveTo((0.25 * this.radius) * scaleFactor, (-0.3 * this.radius) * scaleFactor); ctx.lineTo((0.05 * this.radius) * scaleFactor, (-0.2 * this.radius) * scaleFactor); ctx.lineTo((0.25 * this.radius) * scaleFactor, (-0.1 * this.radius) * scaleFactor); ctx.stroke(); } this.drawFace = function(ctx, scaleFactor) { if(this.drawFaceStyle == 1 && Math.random() < 0.05) { this.drawFaceStyle = 2; } else if(this.drawFaceStyle == 2 && Math.random() < 0.1) { this.drawFaceStyle = 1; } if(this.drawEyeStyle == 1 && Math.random() < 0.025) { this.drawEyeStyle = 2; } else if(this.drawEyeStyle == 2 && Math.random() < 0.3) { this.drawEyeStyle = 1; } if(this.middlePointMass.getVelocity() > 0.004) { this.drawOohFace(ctx, scaleFactor); } else { if(this.drawFaceStyle == 1) { this.drawHappyFace1(ctx, scaleFactor, 0.0, -0.3); } else { this.drawHappyFace2(ctx, scaleFactor, 0.0, -0.3); } if(this.drawEyeStyle == 1) { this.drawHappyEyes1(ctx, scaleFactor, 0.0, -0.3); } else { this.drawHappyEyes2(ctx, scaleFactor, 0.0, -0.3); } } } this.getPointMass = function(index) { index += this.pointMasses.length; index = index % this.pointMasses.length; return this.pointMasses[index]; } this.drawBody = function(ctx, scaleFactor) { var i; ctx.strokeStyle = "#000000"; if(this.selected == true) { ctx.fillStyle = "#FFCCCC"; } else { ctx.fillStyle = "#FFFFFF"; } ctx.lineWidth = 5; ctx.beginPath(); ctx.moveTo(this.pointMasses[0].getXPos() * scaleFactor, this.pointMasses[0].getYPos() * scaleFactor); for(i = 0; i < this.pointMasses.length; i++) { var px, py, nx, ny, tx, ty, cx, cy; var prevPointMass, currentPointMass, nextPointMass, nextNextPointMass; prevPointMass = this.getPointMass(i - 1); currentPointMass = this.pointMasses[i]; nextPointMass = this.getPointMass(i + 1); nextNextPointMass = this.getPointMass(i + 2); tx = nextPointMass.getXPos(); ty = nextPointMass.getYPos(); cx = currentPointMass.getXPos(); cy = currentPointMass.getYPos(); px = cx * 0.5 + tx * 0.5; py = cy * 0.5 + ty * 0.5; nx = cx - prevPointMass.getXPos() + tx - nextNextPointMass.getXPos(); ny = cy - prevPointMass.getYPos() + ty - nextNextPointMass.getYPos(); px += nx * 0.16; py += ny * 0.16; px = px * scaleFactor; py = py * scaleFactor; tx = tx * scaleFactor; ty = ty * scaleFactor; ctx.bezierCurveTo(px, py, tx, ty, tx, ty); } ctx.closePath(); ctx.stroke(); ctx.fill(); } this.drawSimpleBody = function(ctx, scaleFactor) { for(i = 0; i < this.sticks.length; i++) { this.sticks[i].draw(ctx, scaleFactor); } } this.draw = function(ctx, scaleFactor) { var i; var up, ori, ang; this.drawBody(ctx, scaleFactor); ctx.strokeStyle = "#000000"; ctx.fillStyle = "#000000" ctx.save(); ctx.translate(this.middlePointMass.getXPos() * scaleFactor, (this.middlePointMass.getYPos() - 0.35 * this.radius) * scaleFactor); up = new Vector(0.0, -1.0); ori = new Vector(0.0, 0.0); ori.set(this.pointMasses[0].getPos()); ori.sub(this.middlePointMass.getPos()); ang = Math.acos(ori.dotProd(up) / ori.length()); if(ori.getX() < 0.0) { ctx.rotate(-ang); } else { ctx.rotate(ang); } // this.drawEars(ctx, scaleFactor); this.drawFace(ctx, scaleFactor); ctx.restore(); } } function BlobCollective(x, y, startNum, maxNum) { this.maxNum = maxNum; this.numActive = 1; this.blobs = new Array(); this.tmpForce = new Vector(0.0, 0.0); this.selectedBlob = null; this.blobs[0] = new Blob(x, y, 0.4, 8); this.split = function() { var i, maxIndex = 0, maxRadius = 0.0; var emptySlot; var motherBlob, newBlob; if(this.numActive == this.maxNum) { return; } emptySlot = this.blobs.length; for(i = 0; i < this.blobs.length; i++) { if(this.blobs[i] != null && this.blobs[i].getRadius() > maxRadius) { maxRadius = this.blobs[i].getRadius(); motherBlob = this.blobs[i]; } else if(this.blobs[i] == null) { emptySlot = i; } } motherBlob.scale(0.75); newBlob = new Blob(motherBlob.getXPos(), motherBlob.getYPos(), motherBlob.getRadius(), 8); for(i = 0; i < this.blobs.length; i++) { if(this.blobs[i] == null) { continue; } this.blobs[i].addBlob(newBlob); newBlob.addBlob(this.blobs[i]); } this.blobs[emptySlot] = newBlob; this.numActive++; } this.findSmallest = function(exclude) { var minRadius = 1000.0, minIndex; var i; for(i = 0; i < this.blobs.length; i++) { if(i == exclude || this.blobs[i] == null) { continue; } if(this.blobs[i].getRadius() < minRadius) { minIndex = i; minRadius = this.blobs[i].getRadius(); } } return minIndex; } this.findClosest = function(exclude) { var minDist = 1000.0, foundIndex, dist, aXbX, aYbY; var i; var myPointMass, otherPointMass; myPointMass = this.blobs[exclude].getMiddlePointMass(); for(i = 0; i < this.blobs.length; i++) { if(i == exclude || this.blobs[i] == null) { continue; } otherPointMass = this.blobs[i].getMiddlePointMass(); aXbX = myPointMass.getXPos() - otherPointMass.getXPos(); aYbY = myPointMass.getYPos() - otherPointMass.getYPos(); dist = aXbX * aXbX + aYbY * aYbY; if(dist < minDist) { minDist = dist; foundIndex = i; } } return foundIndex; } this.join = function() { var blob1Index, blob2Index, blob1, blob2; var r1, r2, r3; if(this.numActive == 1) { return; } blob1Index = this.findSmallest(-1); blob2Index = this.findClosest(blob1Index); r1 = this.blobs[blob1Index].getRadius(); r2 = this.blobs[blob2Index].getRadius(); r3 = Math.sqrt(r1 * r1 + r2 * r2); this.blobs[blob1Index] = null; this.blobs[blob2Index].scale(0.945 * r3 / r2); this.numActive--; } this.selectBlob = function(x, y) { var i, minDist = 10000.0; var otherPointMass; var selectedBlob; var selectOffset = null; if(this.selectedBlob != null) { return; } for(i = 0; i < this.blobs.length; i++) { if(this.blobs[i] == null) { continue; } otherPointMass = this.blobs[i].getMiddlePointMass(); aXbX = x - otherPointMass.getXPos(); aYbY = y - otherPointMass.getYPos(); dist = aXbX * aXbX + aYbY * aYbY; if(dist < minDist) { minDist = dist; if(dist < this.blobs[i].getRadius() * 0.5) { this.selectedBlob = this.blobs[i]; selectOffset = { x : aXbX, y : aYbY }; } } } if(this.selectedBlob != null) { this.selectedBlob.setSelected(true); } return selectOffset; } this.unselectBlob = function() { if(this.selectedBlob == null) { return; } this.selectedBlob.setSelected(false); this.selectedBlob = null; } this.selectedBlobMoveTo = function(x, y) { if(this.selectedBlob == null) { return; } this.selectedBlob.moveTo(x, y); } this.move = function(dt) { var i; for(i = 0; i < this.blobs.length; i++) { if(this.blobs[i] == null) { continue; } this.blobs[i].move(dt); } } this.sc = function(env) { var i; for(i = 0; i < this.blobs.length; i++) { if(this.blobs[i] == null) { continue; } this.blobs[i].sc(env); } if(this.blobAnchor != null) { this.blobAnchor.sc(); } } this.setForce = function(force) { var i; for(i = 0; i < this.blobs.length; i++) { if(this.blobs[i] == null) { continue; } if(this.blobs[i] == this.selectedBlob) { this.blobs[i].setForce(new Vector(0.0, 0.0)); continue; } this.blobs[i].setForce(force); } } this.addForce = function(force) { var i; for(i = 0; i < this.blobs.length; i++) { if(this.blobs[i] == null) { continue; } if(this.blobs[i] == this.selectedBlob) { continue; } this.tmpForce.setX(force.getX() * (Math.random() * 0.75 + 0.25)); this.tmpForce.setY(force.getY() * (Math.random() * 0.75 + 0.25)); this.blobs[i].addForce(this.tmpForce); } } this.draw = function(ctx, scaleFactor) { var i; for(i = 0; i < this.blobs.length; i++) { if(this.blobs[i] == null) { continue; } this.blobs[i].draw(ctx, scaleFactor); } } } function debug(msg, okFunc, cancelFunc) { if(confirm(msg) == true && okFunc != null) { okFunc(); } else if(cancelFunc != null) { cancelFunc(); } } var env; var width = 1000.0; var height = 1800.0; var scaleFactor = 200.0; var blobColl; var gravity; var stopped; var savedMouseCoords = null; var selectOffset = null; function update() { var dt = 0.05; if(savedMouseCoords != null && selectOffset != null) { blobColl.selectedBlobMoveTo(savedMouseCoords.x - selectOffset.x, savedMouseCoords.y - selectOffset.y); } blobColl.move(dt); blobColl.sc(env); blobColl.setForce(gravity); } function draw() { var canvas = document.getElementById('blob'); if(canvas.getContext == null) { return; } var ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, width, height); env.draw(ctx, scaleFactor); blobColl.draw(ctx, scaleFactor); } function timeout() { draw(); update(); if(stopped == false) { setTimeout('timeout()', 30); } } function init() { var canvas = document.getElementById('blob'); if(canvas.getContext == null) { alert("You need Firefox version 1.5 or higher for this to work, sorry."); return; } document.onkeydown = function(event) { var keyCode; if(event == null) { keyCode = window.event.keyCode; } else { keyCode = event.keyCode; } switch(keyCode) { // left case 37: blobColl.addForce(new Vector(-50.0, 0.0)); break; // up case 38: blobColl.addForce(new Vector(0.0, -50.0)); break; // right case 39: blobColl.addForce(new Vector(50.0, 0.0)); break; // down case 40: blobColl.addForce(new Vector(0.0, 50.0)); break; // join 'j' case 74: blobColl.join(); break; // split 'h' case 72: blobColl.split(); break; // toggle gravity 'g' case 71: toggleGravity(); break; default: break; } } function getMouseCoords(event) { if(event == null) { event = window.event; } if(event == null) { return null; } if(event.pageX || event.pageY){ return {x:event.pageX / scaleFactor, y:event.pageY / scaleFactor}; } return null; } document.onmousedown = function(event) { var mouseCoords; if(stopped == true) { return; } mouseCoords = getMouseCoords(event); if(mouseCoords == null) { return; } selectOffset = blobColl.selectBlob(mouseCoords.x, mouseCoords.y); } document.onmouseup = function(event) { blobColl.unselectBlob(); savedMouseCoords = null; selectOffset = null; } document.onmousemove = function(event) { var mouseCoords; if(stopped == true) { return; } if(selectOffset == null) { return; } mouseCoords = getMouseCoords(event); if(mouseCoords == null) { return; } blobColl.selectedBlobMoveTo(mouseCoords.x - selectOffset.x, mouseCoords.y - selectOffset.y); savedMouseCoords = mouseCoords; } env = new Environment(0.2, 0.2, 2.6, 1.6); blobColl = new BlobCollective(1.0, 1.0, 1, 200); gravity = new Vector(0.0, 10.0); stopped = false; timeout(); } function stop() { stopped = true; } function start() { stopped = false; timeout(); } function toggleGravity() { if(gravity.getY() > 0.0) { gravity.setY(0.0); } else { gravity.setY(10.0); } } </script> <style type="text/css"> canvas { } </style> </head> <body onload="init();"> <canvas id="blob" width="1000" height="800"></canvas><br> <a href="javascript:stop()"></a> <a href="javascript:start()"></a> </body> </head>
文/闫鑫原创
Meet so Meet.
C plusplus
I-PLUS....