Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it available for in browser usage #13

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
427 changes: 224 additions & 203 deletions Cloth.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# [Tearable Cloth](https://dacuteraccoon.github.io/Tearable-Cloth/dist/)
A tearable cloth simulation using vertlet integration.

### [dissimulate/Tearable-Cloth](https://github.com/dissimulate/Tearable-Cloth)
39 changes: 39 additions & 0 deletions dist/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>CodePen - Tearable Cloth</title>
<link rel="stylesheet" href="./style.css">

</head>
<body>
<!-- partial:index.partial.html -->
<canvas id="c"></canvas>

<div id="info">
<div id="top">
<a id="close" href="">&times;</a>
</div>
<p>
<br>- Tear the cloth with your mouse
<br>
<br>- Right click and drag to cut
<br>
<br>
<br>
<div class="center">
<a id="github" target="_blank" href="https://github.com/dissimulate/Tearable-Cloth">GitHub</a>&nbsp;<!--span class="bull">&bull;</span>&nbsp;<a id="twitter" target="_blank" href="https://twitter.com/abro_oks">@abro_oks</a-->
</div>
<br>
</p>
</div>

<!--div id="new">
Wobble some <a target="_blank" href="https://codepen.io/dissimulate/details/dJgMaO">jelly</a> <span class="bull">&bull;</span>
Check out my <a target="_blank" href="https://www.instagram.com/abro_oks/">instagram!</a>
</div-->
<!-- partial -->
<script src="./script.js"></script>

</body>
</html>
256 changes: 256 additions & 0 deletions dist/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
document.getElementById('close').onmousedown = function(e) {
e.preventDefault();
document.getElementById('info').style.display = 'none';
return false;
};

// settings

var physics_accuracy = 3,
mouse_influence = 20,
mouse_cut = 5,
gravity = 1200,
cloth_height = 30,
cloth_width = 50,
start_y = 20,
spacing = 7,
tear_distance = 60;


window.requestAnimFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};

var canvas,
ctx,
cloth,
boundsx,
boundsy,
mouse = {
down: false,
button: 1,
x: 0,
y: 0,
px: 0,
py: 0
};

var Point = function (x, y) {
this.x = x;
this.y = y;
this.px = x;
this.py = y;
this.vx = 0;
this.vy = 0;
this.pin_x = null;
this.pin_y = null;

this.constraints = [];
};

Point.prototype.update = function (delta) {
if (mouse.down) {
var diff_x = this.x - mouse.x,
diff_y = this.y - mouse.y,
dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y);

if (mouse.button == 1) {
if (dist < mouse_influence) {
this.px = this.x - (mouse.x - mouse.px) * 1.8;
this.py = this.y - (mouse.y - mouse.py) * 1.8;
}

} else if (dist < mouse_cut) this.constraints = [];
}

this.add_force(0, gravity);

delta *= delta;
nx = this.x + ((this.x - this.px) * .99) + ((this.vx / 2) * delta);
ny = this.y + ((this.y - this.py) * .99) + ((this.vy / 2) * delta);

this.px = this.x;
this.py = this.y;

this.x = nx;
this.y = ny;

this.vy = this.vx = 0
};

Point.prototype.draw = function () {
if (!this.constraints.length) return;

var i = this.constraints.length;
while (i--) this.constraints[i].draw();
};

Point.prototype.resolve_constraints = function () {
if (this.pin_x != null && this.pin_y != null) {
this.x = this.pin_x;
this.y = this.pin_y;
return;
}

var i = this.constraints.length;
while (i--) this.constraints[i].resolve();

this.x > boundsx ? this.x = 2 * boundsx - this.x : 1 > this.x && (this.x = 2 - this.x);
this.y < 1 ? this.y = 2 - this.y : this.y > boundsy && (this.y = 2 * boundsy - this.y);
};

Point.prototype.attach = function (point) {
this.constraints.push(
new Constraint(this, point)
);
};

Point.prototype.remove_constraint = function (constraint) {
this.constraints.splice(this.constraints.indexOf(constraint), 1);
};

Point.prototype.add_force = function (x, y) {
this.vx += x;
this.vy += y;

var round = 400;
this.vx = ~~(this.vx * round) / round;
this.vy = ~~(this.vy * round) / round;
};

Point.prototype.pin = function (pinx, piny) {
this.pin_x = pinx;
this.pin_y = piny;
};

var Constraint = function (p1, p2) {
this.p1 = p1;
this.p2 = p2;
this.length = spacing;
};

Constraint.prototype.resolve = function () {
var diff_x = this.p1.x - this.p2.x,
diff_y = this.p1.y - this.p2.y,
dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y),
diff = (this.length - dist) / dist;

if (dist > tear_distance) this.p1.remove_constraint(this);

var px = diff_x * diff * 0.5;
var py = diff_y * diff * 0.5;

this.p1.x += px;
this.p1.y += py;
this.p2.x -= px;
this.p2.y -= py;
};

Constraint.prototype.draw = function () {
ctx.moveTo(this.p1.x, this.p1.y);
ctx.lineTo(this.p2.x, this.p2.y);
};

var Cloth = function () {
this.points = [];

var start_x = canvas.width / 2 - cloth_width * spacing / 2;

for (var y = 0; y <= cloth_height; y++) {
for (var x = 0; x <= cloth_width; x++) {
var p = new Point(start_x + x * spacing, start_y + y * spacing);

x != 0 && p.attach(this.points[this.points.length - 1]);
y == 0 && p.pin(p.x, p.y);
y != 0 && p.attach(this.points[x + (y - 1) * (cloth_width + 1)])

this.points.push(p);
}
}
};

Cloth.prototype.update = function () {
var i = physics_accuracy;

while (i--) {
var p = this.points.length;
while (p--) this.points[p].resolve_constraints();
}

i = this.points.length;
while (i--) this.points[i].update(.016);
};

Cloth.prototype.draw = function () {
ctx.beginPath();

var i = cloth.points.length;
while (i--) cloth.points[i].draw();

ctx.stroke();
};

function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);

cloth.update();
cloth.draw();

requestAnimFrame(update);
}

function start() {
canvas.onmousedown = function (e) {
mouse.button = e.which;
mouse.px = mouse.x;
mouse.py = mouse.y;
var rect = canvas.getBoundingClientRect();
mouse.x = e.clientX - rect.left,
mouse.y = e.clientY - rect.top,
mouse.down = true;
e.preventDefault();
};

canvas.onmouseup = function (e) {
mouse.down = false;
e.preventDefault();
};

canvas.onmousemove = function (e) {
mouse.px = mouse.x;
mouse.py = mouse.y;
var rect = canvas.getBoundingClientRect();
mouse.x = e.clientX - rect.left,
mouse.y = e.clientY - rect.top,
e.preventDefault();
};

canvas.oncontextmenu = function (e) {
e.preventDefault();
};

boundsx = canvas.width - 1;
boundsy = canvas.height - 1;

ctx.strokeStyle = '#888';

cloth = new Cloth();

update();
}

window.onload = function () {
canvas = document.getElementById('c');
ctx = canvas.getContext('2d');

canvas.width = 560;
canvas.height = 350;

start();
};
Loading