Webbing (canvas demo)
by Jason Tiscione
HTML
<html lang='en'>
<head>
<meta charset='utf-8'>
<title>Webbing</title>
<link rel='stylesheet' type='text/css' href='webbing.css'>
</head>
<body>
<h1>Webbing</h1>
<canvas id ='canvas' width='780' height='520'>
Canvas not supported
</canvas>
<div>
<div class='controllerpane'>
<div>
<label class='rangeLabel' for='springConstantRange'>Tension: </label>
<div class='minmaxlabel'>MIN</div>
<input type='range' id='springConstantRange' min='0.1' value='0.3' step='0.01' max='0.5'/>
<div class='minmaxlabel'>MAX</div>
<input class='defaultButton' type='button' id='springConstantDefaultButton' value='default' disabled='true'/>
</div>
<div>
<label class='rangeLabel' for='dampingCoefficientRange'>Viscosity:</label>
<div class="minmaxlabel">MIN</div>
<input type='range' id='dampingCoefficientRange' min='0.001' value='0.005' step='0.001' max='0.009'/>
<div class='minmaxlabel'>MAX</div>
<input class='defaultButton' type='button' id='dampingCoefficientDefaultButton' value='default' disabled='true'/>
</div>
</div>
<div class='controllerpane'>
<div>Lattice type:
<input type="radio" name="lattice_type" id="lattice_triangular" value="triangle">
<label for="lattice_triangular">Triangles</label>
<input type="radio" name="lattice_type" id="lattice_square" value="square">
<label for="lattice_square">Squares</label>
<input type="radio" name="lattice_type" id="lattice_hexagonal" value="hexagon" checked='checked'>
<label for="lattice_hexagonal">Hexagons</label>
</div>
<div>
<div style='display:inline-block;text-align:left'>
<input type='checkbox' id='pinsCheckbox' checked='checked'>
<label for='pins'>Pin down mouse drags</label><br/>
<a id='clearPinsLink' style='width:180px;cursor:pointer'>Clear pins</a>
</div>
<div style='display:inline-block;margin-left:60px'>
<input type='button' id='pauseResumeButton'...
CSS
html, body {
background: #aaaaaa;
font: normal 14px/16px Helvetica, sans-serif;
text-align:center;
}
h1 {
margin:10px;
text-align: center;
}
canvas {
background: #404080;
border: thin inset #808080;
cursor: crosshair;
}
.controllerpane {
display: inline-block;
vertical-align: top;
width: 380px;
margin-top: 5px;
margin-left: 15px;
}
.rangeLabel {
display: inline-block;
width: 60px;
margin-right: 10px;
text-align: right;
}
.minmaxlabel {
display: inline-block;
width: 30px;
text-align: center;
}
.footer {
color: #202020;
text-align: right;
font-style: italic;
margin-top: 5px;
margin-bottom: 5px;
margin-right: 15px;
}
JavaScript
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
canvasWidth = canvas.width,
canvasHeight = canvas.height,
REFRESH_INTERVAL = 20,
SEPARATION = 10,
DEFAULT_SPRING_CONSTANT = springConstantRange.value,
DEFAULT_DAMPING_COEFFICIENT = dampingCoefficientRange.value,
SPRING_CONSTANT = DEFAULT_SPRING_CONSTANT,
DAMPING_COEFFICIENT = DEFAULT_DAMPING_COEFFICIENT,
allowPins = true,
paused = true,
draggedAtom = null,
lattice = new Array(),
atomCount = 0,
neighborCount = 0;
initialize();
var loop = setInterval(function() {
if (!paused) {
move();
draw()
}
}, REFRESH_INTERVAL);
function initialize(latticeType) {
context.strokeStyle = 'goldenrod';
context.fillStyle = 'red';
pinsCheckbox.value = true;
clearPinsLink.style.color = 'gray';
lattice_triangular.checked = true;
setAllowPins(true);
resetSpringConstant();
resetDampingCoefficient();
constructTriangularLattice();
setPaused(false);
paused = true;
}
function Atom(x, y) {
this.x = x;
this.y = y;
this.vx = 0;
this.vy = 0;
this.neighbors = new Array();
/*
If neighbors[i] lies across northern border, vertEdgeCrossings[i] is -1
If neighbors[i] lies across southern border, vertEdgeCrossings[i] is 1
If neighbors[i] lies across western border, horizEdgeCrossings[i] is -1
If neighbors[i] lies across eastern border, horizEdgeCrossings[i] is 1
Default values are zero
*/
this.vertEdgeCrossings = new Array();
this.horizEdgeCrossings = new Array();
this.immobile = false;
}
function constructTriangularLattice() {
lattice = new Array();
atomCount = 0;
neighborCount = 6;
SEPARATION = 20;
var EQUILATERAL_TRIANGLE_HEIGHT = SEPARATION * Math.sin(Math.PI / 3.0);
// Slide unit cell boundaries away from atoms;
// each unit cell top corner is located halfway down a SSE triangle edge
var VERTEX_OFFSET_X = (0.25 * SEPARATION),
...