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:&nbsp;&nbsp;
					<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),
     ...