Created
March 13, 2015 21:57
-
-
Save archonic/928b1aabb65cd4989648 to your computer and use it in GitHub Desktop.
Paper.js Fluid Simulation with particles (doesn't do a great job of it)
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
<html> | |
<head> | |
<title>Fluid simulation with balls</title> | |
<script type="text/javascript" src="paper-full.js"></script> | |
<!-- Define inlined PaperScript associate it with myCanvas --> | |
<script type="text/paperscript" canvas="myCanvas"> | |
// Ball variables | |
radius = 20; | |
totalBalls = 100; | |
gravity = 0.3; | |
decay = 0.5; | |
function Ball(r, p, v) { | |
this.radius = r; | |
this.point = p; | |
this.vector = v; | |
this.maxVec = 15; | |
this.numSegment = Math.floor(r / 3 + 2); | |
this.boundOffset = []; | |
this.boundOffsetBuff = []; | |
this.sidePoints = []; | |
this.path = new Path({ | |
fillColor: { | |
hue: Math.random() * 360, | |
saturation: 1, | |
brightness: 1 | |
}, | |
blendMode: 'screen' | |
}); | |
for (var i = 0; i < this.numSegment; i ++) { | |
this.boundOffset.push(this.radius); | |
this.boundOffsetBuff.push(this.radius); | |
this.path.add(new Point()); | |
this.sidePoints.push(new Point({ | |
angle: 360 / this.numSegment * i, | |
length: 1 | |
})); | |
} | |
} | |
function getVectorAwayFrom(ball, wall) { | |
var size = view.size; | |
switch(wall) { | |
case "top": | |
wallPoint = new Point(ball.point.x, 0); | |
break; | |
case "bottom": | |
wallPoint = new Point(ball.point.x, size.height); | |
break; | |
case "left": | |
wallPoint = new Point(0, ball.point.y) | |
break; | |
case "right": | |
wallPoint = new Point(size.width, ball.point.y); | |
break; | |
} | |
//drawPoint(wallPoint); | |
var distFromWall = ball.point.getDistance(wallPoint); | |
var overlap = ball.radius - distFromWall; | |
var vectorAway = ( ball.point - wallPoint ).normalize(overlap * 0.95); | |
//console.log('vectorAway: ' + vectorAway); | |
return vectorAway; | |
} | |
Ball.prototype = { | |
iterate: function() { | |
this.checkBorders(); | |
// Limit top speed | |
if (this.vector.length > this.maxVec) | |
this.vector.length = this.maxVec; | |
// apply gravity to Y vector | |
this.vector.y += gravity; | |
// Apply the vector to the point to have it animate | |
this.point += this.vector; | |
//console.log('Current vector: ' + this.vector); | |
this.updateShape(); | |
}, | |
checkBorders: function() { | |
var size = view.size; | |
// Left border | |
if (this.point.x < this.radius) { | |
this.vector += getVectorAwayFrom(this, "left"); | |
this.vector.x *= decay; | |
} | |
// Right border | |
if (this.point.x > size.width - this.radius) { | |
this.vector += getVectorAwayFrom(this, "right"); | |
this.vector.x *= decay; | |
} | |
// Top border | |
if (this.point.y < this.radius) { | |
this.vector += getVectorAwayFrom(this, "top"); | |
this.vector.y *= decay; | |
} | |
// Bottom border | |
if (this.point.y > size.height - this.radius) { | |
this.vector += getVectorAwayFrom(this, "bottom"); | |
this.vector.y *= decay; | |
} | |
}, | |
updateShape: function() { | |
var segments = this.path.segments; | |
for (var i = 0; i < this.numSegment; i ++) | |
segments[i].point = this.getSidePoint(i); | |
this.path.smooth(); | |
for (var i = 0; i < this.numSegment; i ++) { | |
if (this.boundOffset[i] < this.radius / 4) | |
this.boundOffset[i] = this.radius / 4; | |
var next = (i + 1) % this.numSegment; | |
var prev = (i > 0) ? i - 1 : this.numSegment - 1; | |
var offset = this.boundOffset[i]; | |
offset += (this.radius - offset) / 15; | |
offset += ((this.boundOffset[next] + this.boundOffset[prev]) / 2 - offset) / 3; | |
this.boundOffsetBuff[i] = this.boundOffset[i] = offset; | |
} | |
}, | |
react: function(b) { | |
var dist = this.point.getDistance(b.point); | |
if (dist < this.radius + b.radius && dist != 0) { | |
var overlap = this.radius + b.radius - dist; | |
var direc = (this.point - b.point).normalize(overlap * 1); | |
this.vector += direc; | |
this.vector.x *= decay; | |
this.vector.y *= decay; | |
b.vector -= direc; | |
b.vector.x *= decay; | |
b.vector.y *= decay; | |
} | |
}, | |
getBoundOffset: function(b) { | |
var diff = this.point - b; | |
var angle = (diff.angle + 180) % 360; | |
return this.boundOffset[Math.floor(angle / 360 * this.boundOffset.length)]; | |
}, | |
getSidePoint: function(index) { | |
return this.point + this.sidePoints[index] * this.boundOffset[index]; | |
} | |
} | |
// Container which holds all balls | |
var allBalls = []; | |
for(var i = 0; i < totalBalls; i++) { | |
var position = new Point.random() * view.size; | |
var vector = new Point({ | |
angle: 360 * Math.random(), | |
length: Math.random() * 10 | |
}); | |
allBalls.push(new Ball(radius, position, vector)); | |
} | |
console.log('Finished creating all balls'); | |
// Define and fill our cursor | |
//cursor = new Path.Circle(new Point(0,0), 25); | |
//cursor.fillColor = 'blue'; | |
// This function is called whenever the user | |
// move the mouse in the view | |
function onMouseMove(event) { | |
// Have our "circle cursor" follow the mouse | |
//cursor.position = event.point; | |
//checkForCollisions(); | |
} | |
function showIntersections(path1, path2) { | |
var intersections = path1.getIntersections(path2); | |
for (var i = 0; i < intersections.length; i++) { | |
drawPoint(intersections[i]); | |
} | |
} | |
function drawPoint(point) { | |
new Path.Circle({ | |
center: point, | |
radius: 5, | |
fillColor: '#009dec' | |
}).removeOnMove(); | |
} | |
function onFrame() { | |
for (var i = 0; i < allBalls.length - 1; i++) { | |
for (var j = i + 1; j < allBalls.length; j++) { | |
allBalls[i].react(allBalls[j]); | |
} | |
} | |
for (var i = 0, l = allBalls.length; i < l; i++) { | |
allBalls[i].iterate(); | |
//checkForCollisions(); | |
} | |
//console.log('Finished frame'); | |
} | |
function checkForCollisions() { | |
// Are there any circles that intersect with our mouse? | |
for (var i = 0, l = allBalls.length; i < l; i++) { | |
showIntersections(allBalls[i].path, cursor); | |
} | |
} | |
</script> | |
</head> | |
<body style="margin:0;padding:0;"> | |
<canvas id="myCanvas" resize style="width:99%;height:90%;border:1px #000 solid;"></canvas> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment