Verlet
by Tiffany Rayside
<div class = 'svg'>
<div class = 'svg'>
<svg width="465" height="465" viewBox = '-20 0 465 465'>
<path id="p" d="M 100 350 L 200 50 300 350 Z" stroke="hsla(231, 5%, 85%, 1)" stroke-width="1" fill="none" />
</svg>
<br />
<div class = 'input'>
<form oninput="val.value=ptrng.value">
<label>#Points <input id="ptrng" type="range" min="0" max="20" step="1"/>
<output name="val" for="ptrng"></output></label>
</form>
</div>
<p>*Note:<br>There are boundary issues within the container.<br>The object may get lost when it hits certain points.<br>Just adjust the slider && it will return into view.</p>
</div>
@import url(http://fonts.googleapis.com/css?family=Ubuntu:700);
@import url(http://fonts.googleapis.com/css?family=Ubuntu:700);
html, body {
height: 100%;
}
body {
font-family: 'Ubuntu', sans-serif;
padding: 0;
margin: 0;
overflow:hidden;
background-repeat: no-repeat;
background-attachment: fixed;
background: #282537;
background-image: radial-gradient(top, circle cover, #3c3b52 0%, #252233 80%);
background-image: radial-gradient(top, circle cover, #3c3b52 0%, #252233 80%);
background-image:radial-gradient(top, circle cover, #3c3b52 0%, #252233 80%);
cursor: move;
user-select:none;
user-select:none;
user-select:none;
user-select:none;
}
svg{
background-repeat: no-repeat;
background-attachment: fixed;
background: #282537;
background-image: radial-gradient(top, circle cover, #3c3b52 0%, #252233 80%);
background-image: radial-gradient(top, circle cover, #3c3b52 0%, #252233 80%);
box-shadow:inset 4px 4px 8px 8px hsla(0,5%,5%,.07);
border-radius:5px;
}
.svg{
width:100%;
text-align:center;
margin-top:2%;
}
.input{
padding:2em;
}
p{
text-align:left;
width:90%;
line-height:1.5em;
padding:.5em;
}
form,p{
color:hsla(231, 5%, 85%, 1);
}
var ss = 460; //svg size
var ss = 460; //svg size
var p = document.getElementById('p'),
poly,
$,
FPS = 60;
function Pt(x, y) {
this.x = x || 0;
this.y = y || 0;
}
Pt.dist = function(a, b) {
return Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));
};
function Rect(x, y, w, h) {
this.x = 0;
this.y = 0;
this.w = w || 0; //width
this.h = h || 0; //height
this.l = x; //left
this.r = x + w; //right
this.t = y; //top
this.b = y + h; //bottom
}
//verlet point
function vPt(x, y) {
this.x = x || 0;
this.y = y || 0;
this.px = this.x;
this.py = this.y;
}
//update
vPt.prototype.upd = function() {
var tmpX = this.x,
tmpY = this.y;
this.x += this.getX();
this.y += this.getY();
this.px = tmpX;
this.py = tmpY;
}
//set position
vPt.prototype.setPos = function(x, y) {
this.x = this.px = x;
this.y = this.py = y;
}
//contain
vPt.prototype.cont = function(rect) {
this.x = Math.max(rect.l, Math.min(rect.r, this.x));
this.y = Math.max(rect.t, Math.min(rect.b, this.y));
}
//set verlet x
vPt.prototype.setX = function(val) {
this.px = this.x - val;
}
//get verlet x
vPt.prototype.getX = function() {
return this.x - this.px;
}
//set verlet y
vPt.prototype.setY = function(val) {
this.py = this.y - val;
}
//get verlet y
vPt.prototype.getY = function() {
return this.y - this.py;
}
//verlet lines
function vLine(ptA, ptB, k, length) {
this.ptA = ptA;
this.ptB = ptB;
this.k = k || 0.5;
this.length = length || Pt.dist(ptA, ptB);
}
//update lines
vLine.prototype.upd = function() {
var dx = this.ptB.x - this.ptA.x,
dy = this.ptB.y - this.ptA.y,
dist = Math.sqrt(dx * dx + dy * dy),
diff = (this.length - dist) / dist,
offX = diff * dx * this.k,
offY = diff * dy * this.k;
this.ptA.x -= offX;
this.ptA.y -= offY;
this.ptB.x += offX;
this.ptB.y += offY;
}
//polygon (point, vertex, size, var k)
function Poly(pt, vert, size, k) {
this.vert = vert;
this.pts = [];
this.lines = [];
this.vx = 0.2;
for (var v = 0; v < 2 * Math.PI; v += 2 * Math.PI / vert) {
this.pts.push(
new vPt(Math.cos(v) * size + pt.x, Math.sin(v) * size + pt.y)
);
}
for (var i = 0, n = this.pts.length; i < n; i++) {
pt = this.pts[i];
for (var j = 0, m = this.pts.length; j < m; j++) {
if (i !== j) {
this.lines.push(
new vLine(pt, this.pts[j], k)
);
}
}
}
}
Poly.prototype.upd = function() {
var pt,
line,
i = 0,
n = this.pts.length;
for (; i < n; i++) {
pt = this.pts[i];
pt.y += this.vx;
pt.upd();
pt.cont($);
}
i = 0;
n = this.lines.length;
for (; i < n; i++) {
line = this.lines[i];
line.upd();
}
}
Poly.prototype.draw = function() {
var pt,
line,
i,
n;
var d = 'M ' + this.pts[0].x + ' ' + this.pts[0].y + ' L ';
i = 0;
n = this.pts.length;
for (; i < n; i++) {
pt = this.pts[i];
d += ' ' + pt.x + ' ' + pt.y;
}
d += ' Q ';
i = 0;
n = this.lines.length;
for (; i < n; i++) {
line = this.lines[i];
d += ' ' + line.ptA.x + ' ' + line.ptA.y;
d += ' ' + line.ptB.x + ' ' + line.ptB.y;
}
d += ' Z';
p.setAttribute('d', d);
}
var ms = {
msup: 'mouseup',
msmv: 'mousemove',
msdn: 'mousedown'
}
document.addEventListener(ms.msdn, onMouseDown);
document.addEventListener(ms.msup, onMouseUp);
document.addEventListener(ms.msmv, onMouseMove);
var msdn,
prevX = 0,
prevY = 0;
function onMouseDown(e) {
msdn = true;
prevX = e.clientX;
prevY = e.clientY;
}
function onMouseUp(e) {
msdn = false;
}
function onMouseMove(e) {
if (msdn) {
for (var i = 0; i < poly.pts.length; i++) {
poly.pts[i].x += (e.clientX - prevX) / 8;
poly.pts[i].y += (e.clientY - prevY) / 8;
}
prevX = e.clientX;
prevY = e.clientY;
}
}
$ = new Rect(0, 0, ss, ss);
poly = new Poly(new Pt(220, 90), 9, 80, 0.025);
function loop() {
poly.upd();
poly.draw();
}
setInterval(loop, 1000 / FPS);
document.getElementById('ptrng').addEventListener('change', function(e) {
poly = new Poly(new Pt(220, 90), e.currentTarget.value, 80, 0.025);
});