public void run(JGraphFacade graph) {
		// Set the facade to be non-directed, directed graphs produce incorrect
		// results. Store the directed state to reset it after this method
		boolean directed = graph.isDirected();
		graph.setDirected(false);

		// Allocate memory for local cached arrays
		vertexArray = graph.getVertices().toArray();
		dispX = new double[vertexArray.length];
		dispY = new double[vertexArray.length];
		cellLocation = graph.getLocations(vertexArray);
		isMoveable = new boolean[vertexArray.length];
		neighbours = new int[vertexArray.length][];
		radius = new double[vertexArray.length];
		radiusSquared = new double[vertexArray.length];

		// Temporary map used to setup neighbour array of arrays
		Map vertexMap = new Hashtable(vertexArray.length);

		if (forceConstant < 0.001)
			forceConstant = 0.001;
		forceConstantSquared = forceConstant * forceConstant;

		// Create a map of vertices first. This is required for the array of
		// arrays called neighbours which holds, for each vertex, a list of
		// ints which represents the neighbours cells to that vertex as
		// the indices into vertexArray
		for (int i = 0; i < vertexArray.length; i++) {
			// Set up the mapping from array indices to cells
			vertexMap.put(vertexArray[i], new Integer(i));
			Rectangle2D bounds = graph.getBounds(vertexArray[i]);
			// Set the X,Y value of the internal version of the cell to
			// the center point of the vertex for better positioning
			double width = bounds.getWidth();
			double height = bounds.getHeight();
			cellLocation[i][0] += width / 2.0;
			cellLocation[i][1] += height / 2.0;
			radius[i] = Math.min(width, height);
			radiusSquared[i] = radius[i] * radius[i];
		}

		for (int i = 0; i < vertexArray.length; i++) {
			dispX[i] = 0;
			dispY[i] = 0;
			isMoveable[i] = graph.isMoveable(vertexArray[i]);

			// Get lists of neighbours to all vertices, translate the cells
			// obtained in indices into vertexArray and store as an array
			// against the orginial cell index
			Object cellNeighbours[] = graph.getNeighbours(vertexArray[i], null,
					false).toArray();
			neighbours[i] = new int[cellNeighbours.length];

			for (int j = 0; j < cellNeighbours.length; j++) {
				Integer indexOtherCell = (Integer) vertexMap
						.get(cellNeighbours[j]);
				// Check the connected cell in part of the vertex list to be
				// acted on by this layout
				if (indexOtherCell != null) {
					int k = indexOtherCell.intValue();
					neighbours[i][j] = k;
				} else {
					// The index of the other cell doesn't correspond to any
					// cell listed to be acted upon in this layout. Set the
					// index to the value of this vertex (a dummy self-loop)
					// so the attraction force of the edge is not calculated
					neighbours[i][j] = i;
				}
			}
		}

		temperature = initialTemp;

		// If max number of iterations has not been set, guess it
		if (maxIterations == 0) {
			maxIterations = 20 * (int) Math.sqrt(vertexArray.length);
		}
		progress.reset(maxIterations);
		// Main iteration loop
		for (iteration = 0; iteration < maxIterations && !progress.isStopped(); iteration++) {
			progress.setProgress(iteration);

			// Calculate repulsive forces on all vertices
			calcRepulsion();

			// Calculate attractive forces through edges
			calcAttraction();

			calcPositions();

			reduceTemperature();
		}

		// Moved cell location back to top-left from center locations used in
		// algorithm
		for (int i = 0; i < vertexArray.length; i++) {
			Rectangle2D bounds = graph.getBounds(vertexArray[i]);
			cellLocation[i][0] -= bounds.getWidth() / 2.0;
			cellLocation[i][1] -= bounds.getHeight() / 2.0;
		}

		// Write result back
		graph.setLocations(vertexArray, cellLocation);

		// Reset the directed state of the facade
		graph.setDirected(directed);
	}