text:
package com.hotf.client.view.control;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Stack;

import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d.LineCap;
import com.google.gwt.canvas.dom.client.Context2d.LineJoin;
import com.google.gwt.canvas.dom.client.Context2d.TextAlign;
import com.google.gwt.canvas.dom.client.Context2d.TextBaseline;
import com.google.gwt.canvas.dom.client.CssColor;
import com.google.gwt.canvas.dom.client.FillStrokeStyle;
import com.google.gwt.canvas.dom.client.ImageData;
import com.google.gwt.canvas.dom.client.TextMetrics;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.user.client.ui.Widget;
import com.hotf.client.ClientFactory;
import com.hotf.client.ImageLoader;
import com.hotf.client.event.MessageEvent;
import com.hotf.client.event.MessageEvent.Level;
import com.hotf.client.view.dialog.TextInputDialog;
import com.hotf.client.view.dialog.UploadDialog;

public class MapEditor extends Composite {

	private static final String UPGRADE_MESSAGE = "Please upgrade or change your browser to one that supports the HTML5 Canvas element";
	private static final String SHIFT_MESSAGE = "Press SHIFT key and click again to create a straight line to another node, or press CTRL for a curved line.";
	private static final String PLACE_SIZE_MESSAGE = "A place must be between 100 and 1000 pixels in width and height";
	private static final double MAX_RADIANS = 2.0D * Math.PI;
	private static final double ROTATION_DRAG_LENGTH = 300.0D;
	private static final int MIN_SIZE = 600;

	interface MyUiBinder extends UiBinder<Widget, MapEditor> {
	}

	private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

	private ClientFactory clientFactory;
	private PencilShaper pencilShaper = new PencilShaper();
	private LineShaper lineShaper = new LineShaper();
	private PolyShaper polyShaper = new PolyShaper();
	private FilledPolyShaper filledPolyShaper = new FilledPolyShaper();
	private ImageShaper imageShaper = new ImageShaper();
	private TextShaper textShaper = new TextShaper();
	private ImageElement image;
	private boolean drawing = false;
	private boolean control = false;
	private List<Handle> handles = new ArrayList<Handle>();
	private List<Boolean> curverHandles = new ArrayList<Boolean>();
	private String text;

	@UiField Label imageSize;
	@UiField Image imageField;
	@UiField Image paletteField;
	@UiField ToggleButton pencilButton;
	@UiField AbsolutePanel handlePanel;
	@UiField (provided=true) Canvas canvas;
	@UiField TextBox widthField;
	@UiField TextBox heightField;
	@UiField TextBox strokeSizeField;
	@UiField ColorField strokeStyleField;
	@UiField ColorField fillStyleField;
	@UiField ColorField textStyleField;
	@UiField ListBox strokeCapField;
	@UiField ListBox strokeJoinField;

	public MapEditor(ClientFactory clientFactory) {

		super();

		this.clientFactory = clientFactory;

		canvas = Canvas.createIfSupported();

		if (canvas == null) {
			initWidget(new Label(UPGRADE_MESSAGE));
			return;
		}

		initWidget(uiBinder.createAndBindUi(this));

		for (LineCap c : LineCap.values()) {
			strokeCapField.addItem(c.getValue());
		}
		for (LineJoin j : LineJoin.values()) {
			strokeJoinField.addItem(j.getValue());
		}
		setWidth(MIN_SIZE);
		setHeight(MIN_SIZE);
		widthField.setValue(Integer.toString(MIN_SIZE));
		heightField.setValue(Integer.toString(MIN_SIZE));
		strokeStyleField.setValue(CssColor.make("#000000"));
		fillStyleField.setValue(CssColor.make("#000000"));
		textStyleField.setValue(CssColor.make("#000000"));
		strokeJoinField.setSelectedIndex(getLineJoinIndex("miter"));
		strokeCapField.setSelectedIndex(getLineCapIndex("square"));
		strokeSizeField.setValue("6");
		configureCanvas();

	}

	private void configureCanvas() {
		canvas.getContext2d().setFillStyle(fillStyleField.getValue());
		canvas.getContext2d().setStrokeStyle(strokeStyleField.getValue());
		canvas.getContext2d().setLineJoin(strokeJoinField.getValue(strokeJoinField.getSelectedIndex()));
		canvas.getContext2d().setLineCap(strokeCapField.getValue(strokeCapField.getSelectedIndex()));
		setStrokeSize();
		updatePalette();
	}

	private void setWidth() {
		String value = widthField.getValue();
		Integer width = null;
		try {
			width = Integer.parseInt(value);
			setWidth(width);
		} catch (NumberFormatException e) {
			clientFactory.getEventBus().fireEvent(new MessageEvent(Level.ERROR, "Width must be a number"));
			return;
		}
	}

	private void setWidth(int w) {
		savePoint();
		canvas.setWidth(w + "px");
		handlePanel.setWidth(w + "px");
		canvas.setCoordinateSpaceWidth(w);
		configureCanvas();
		undoMap();
	}

	private void setHeight() {
		String value = heightField.getValue();
		Integer height = null;
		try {
			height = Integer.parseInt(value);
			setHeight(height);
		} catch (NumberFormatException e) {
			clientFactory.getEventBus().fireEvent(new MessageEvent(Level.ERROR, "Height must be a number"));
			return;
		}
	}

	private void setHeight(int h) {
		savePoint();
		canvas.setHeight(h + "px");
		handlePanel.setHeight(h + "px");
		canvas.setCoordinateSpaceHeight(h);
		configureCanvas();
		undoMap();
	}

	private void setStrokeSize() {
		String s = strokeSizeField.getValue();
		try {
			int size = Integer.parseInt(s);
			canvas.getContext2d().setLineWidth((double)size);
			redraw();
		} catch (NumberFormatException e) {
			clientFactory.getEventBus().fireEvent(new MessageEvent(Level.ERROR, "Stroke size must be a number"));
			return;
		}
	}

	private int getLineJoinIndex(String join) {
		LineJoin[] v = LineJoin.values();
		String j = join.toUpperCase();
		for (int i = 0; i < v.length; i++) {
			if (v[i].toString().equals(j)) {
				return i;
			}
		}
		return 0;
	}

	private int getLineCapIndex(String cap) {
		LineCap[] v = LineCap.values();
		String c = cap.toUpperCase();
		for (int i = 0; i < v.length; i++) {
			if (v[i].toString().equals(c)) {
				return i;
			}
		}
		return 0;
	}

	public String getImageData() {
		return canvas.toDataUrl("image/png");
	};

	public void setImageData(final String map, final String overlay) {

		if (canvas == null) {
			return;
		}

		//Default map size
		widthField.setValue(Integer.toString(MIN_SIZE));
		heightField.setValue(Integer.toString(MIN_SIZE));
		setWidth();
		setHeight();

		//clear to transparent overlay
		clearMap();

		//set image size
		setImageSize();

		//initialise map
		DOM.setStyleAttribute(canvas.getElement(), "backgroundImage", "none");
		DOM.setStyleAttribute(canvas.getElement(), "backgroundRepeat", "noRepeat");
		undo.clear();
		redo.clear();
		clearHandles();
		configureCanvas();

		if (map != null) {

			ImageLoader.loadImages(new String[] { map + "&ts=" + new Date().getTime() }, new ImageLoader.CallBack() {
				public void onImagesLoaded(ImageElement[] imageHandles) {
					widthField.setValue(Integer.toString(imageHandles[0].getWidth()));
					heightField.setValue(Integer.toString(imageHandles[0].getHeight()));
					setWidth();
					setHeight();
					DOM.setStyleAttribute(canvas.getElement(), "backgroundImage", "url('" + map + "')");
					clearMap();
					if (overlay != null) {

						ImageLoader.loadImages(new String[] { overlay + "&ts=" + new Date().getTime() }, new ImageLoader.CallBack() {
							public void onImagesLoaded(ImageElement[] imageHandles) {
								ImageElement img = imageHandles[0];
								widthField.setValue(Integer.toString(img.getWidth()));
								heightField.setValue(Integer.toString(img.getHeight()));
								setWidth();
								setHeight();
								canvas.getContext2d().drawImage(img, 0, 0);
								setImageSize();
							}
						});

					}
				}
			});

		} else {

			if (overlay != null) {

				ImageLoader.loadImages(new String[] { overlay + "&ts=" + new Date().getTime() }, new ImageLoader.CallBack() {
					public void onImagesLoaded(ImageElement[] imageHandles) {
						ImageElement img = imageHandles[0];
						widthField.setValue(Integer.toString(img.getWidth()));
						heightField.setValue(Integer.toString(img.getHeight()));
						setWidth();
						setHeight();
						canvas.getContext2d().drawImage(img, 0, 0);
						setImageSize();
					}
				});

			}

		}

	};

	@UiHandler("resizeButton")
	public void resizeButton(ClickEvent event) {
		if (widthField.getValue() == null || widthField.getValue().length() < 100 || widthField.getValue().length() > 1000) {
			clientFactory.getEventBus().fireEvent(new MessageEvent(Level.INFO, PLACE_SIZE_MESSAGE));
			return;
		}
		if (heightField.getValue() == null || heightField.getValue().length() < 100 || heightField.getValue().length() > 1000) {
			clientFactory.getEventBus().fireEvent(new MessageEvent(Level.INFO, PLACE_SIZE_MESSAGE));
			return;
		}
		configureCanvas();
		setWidth();
		setHeight();
		redraw();
	}

	@UiHandler("strokeJoinField")
	public void strokeJoinField(ChangeEvent event) {
		configureCanvas();
		redraw();
	}

	@UiHandler("strokeCapField")
	public void strokeCapField(ChangeEvent event) {
		configureCanvas();
		redraw();
	}

	@UiHandler("strokeSizeField")
	public void strokeSize(KeyUpEvent event) {
		if (strokeSizeField.getValue() == null || strokeSizeField.getValue().length() <= 0) {
			return;
		}
		configureCanvas();
		redraw();
	}

	@UiHandler("strokeSizeField")
	public void strokeSize(ValueChangeEvent<String> event) {
		configureCanvas();
		redraw();
	}

	private void redraw() {
		if (shaper != null && handles.size() > 0) {
			shaper.redraw(null);
		}
	}

	@UiHandler("strokeStyleField")
	public void strokeStyleField(ValueChangeEvent<FillStrokeStyle> e) {
		configureCanvas();
		redraw();
	}

	@UiHandler("fillStyleField")
	public void fillStyleField(ValueChangeEvent<FillStrokeStyle> e) {
		configureCanvas();
		redraw();
	}

	@UiHandler("undoButton")
	public void undoButton(ClickEvent e) {
		undoMap();
	}

	@UiHandler("redoButton")
	public void redoButton(ClickEvent e) {
		redoMap();
	}

	@UiHandler("clearButton")
	public void clearButton(ClickEvent e) {
		clearMap();
	}

	@UiHandler("pencilButton")
	public void pencilButton(ClickEvent e) {
		toggleShaper(e, pencilShaper);
	}

	@UiHandler("lineButton")
	public void lineButton(ClickEvent e) {
		toggleShaper(e, lineShaper);
	}

	@UiHandler("fillAllButton")
	public void rectangleButton(ClickEvent e) {
		fillMap();
	}

	@UiHandler("polygonButton")
	public void polygonButton(ClickEvent e) {
		toggleShaper(e, polyShaper);
	}

	@UiHandler("filledPolygonButton")
	public void filledPolygonButton(ClickEvent e) {
		toggleShaper(e, filledPolyShaper);
	}

	private boolean erasing = false;

	@UiHandler("eraseButton")
	public void eraseButton(ClickEvent e) {
		toggleShaper(e, pencilShaper);
		erasing = true;
	}

	@UiHandler("erasePolygonButton")
	public void erasePolygonButton(ClickEvent e) {
		toggleShaper(e, filledPolyShaper);
		erasing = true;
	}

	@UiHandler("imageButton")
	public void imageButton(ClickEvent e) {
		toggleShaper(e, imageShaper);
	}

	@UiHandler("textButton")
	public void textButton(ClickEvent e) {
		toggleShaper(e, textShaper);
		final TextInputDialog d = GWT.create(TextInputDialog.class);
		d.addValueChangeHandler(new ValueChangeHandler<String>() {
			@Override
			public void onValueChange(ValueChangeEvent<String> event) {
				String txt = event.getValue();
				if (txt != null && txt.length() > 0) {
					text = txt;
				}
			}
		});
		d.center();
	}

	@UiHandler("imageField")
	public void imageField(ClickEvent e) {
		final UploadDialog d = GWT.create(UploadDialog.class);
		d.setText("Upload Image");
		d.addValueChangeHandler(new ValueChangeHandler<String>() {
			@Override
			public void onValueChange(ValueChangeEvent<String> event) {
				ImageLoader.loadImages(new String[] {event.getValue()}, new ImageLoader.CallBack() {
					public void onImagesLoaded(ImageElement[] imageHandles) {

						imageField.setUrl(imageHandles[0].getSrc());
						image = imageHandles[0];
						strokeStyleField.setLoadedImage(imageHandles[0]);
						fillStyleField.setLoadedImage(imageHandles[0]);
						imageShaper.width = image.getWidth();
						imageShaper.height = image.getHeight();
						imageShaper.rotation = 0.0D;
						d.hide();

					}
				});
			}
		});
		d.center();
	}

	private void updatePalette() {
		Canvas pc = Canvas.createIfSupported();
		pc.setCoordinateSpaceHeight(60);
		pc.setCoordinateSpaceWidth(60);
		pc.getContext2d().setFillStyle(CssColor.make(255,255,255));
		pc.getContext2d().fillRect(0.0D, 0.0D, 60.0D, 60.0D);
		pc.getContext2d().setStrokeStyle(canvas.getContext2d().getStrokeStyle());
		pc.getContext2d().setFillStyle(canvas.getContext2d().getFillStyle());
		pc.getContext2d().setLineWidth(5.0D);
		pc.getContext2d().fillRect(20.0D, 20.0D, 30.0D, 30.D);
		pc.getContext2d().strokeRect(20.0D, 20.0D, 30.0D, 30.D);
		pc.getContext2d().setFont("14pt Calibri");
		pc.getContext2d().setFillStyle(textStyleField.getValue());
		pc.getContext2d().setTextAlign(TextAlign.CENTER);
		pc.getContext2d().setTextBaseline(TextBaseline.MIDDLE);
		pc.getContext2d().fillText("Text", 20.0D, 20.0D, 60.0D);
		paletteField.setUrl(pc.toDataUrl());
	}

	@UiHandler("canvas")
	public void canvas(MouseDownEvent e) {
		e.preventDefault();
		e.stopPropagation();
		int mouseX = e.getRelativeX(canvas.getElement());
		int mouseY = e.getRelativeY(canvas.getElement());
		control = e.getNativeEvent().getCtrlKey();
		if (!e.getNativeEvent().getShiftKey() && !control) {
			savePoint();
			clearHandles();
		}
		shaper.draw(mouseX, mouseY);
		drawing = true;
	}

	@UiHandler("finishButton")
	public void finishButton(ClickEvent e) {
		savePoint();
		clearHandles();
	}

	private void clearHandles() {
		for (int i = 0; i < handles.size(); i++) {
			handles.get(i).removeFromParent();
		}
		handles.clear();
		curverHandles.clear();
	}

	@UiHandler("canvas")
	public void canvas(MouseMoveEvent e) {
		e.preventDefault();
		int mouseX = e.getRelativeX(canvas.getElement());
		int mouseY = e.getRelativeY(canvas.getElement());
		if (drawing) {
			shaper.draw(mouseX, mouseY);
		}
	}

	@UiHandler("canvas")
	public void canvas(MouseUpEvent e) {
		e.preventDefault();
		int mouseX = e.getRelativeX(canvas.getElement());
		int mouseY = e.getRelativeY(canvas.getElement());
		if (drawing) {
			shaper.draw(mouseX, mouseY);
			shaper.stop();
			drawing = false;
			setImageSize();
		}
		control = false;
	}

	@UiHandler("canvas")
	public void canvas(MouseOutEvent e) {
		e.preventDefault();
		int mouseX = e.getRelativeX(canvas.getElement());
		int mouseY = e.getRelativeY(canvas.getElement());
		if (drawing) {
			shaper.draw(mouseX, mouseY);
			shaper.stop();
			drawing = false;
			setImageSize();
		}
		control = false;
	}

	private void setImageSize() {
		int size = (canvas.toDataUrl().getBytes().length * 2) / 1024;
		imageSize.setText("Size " + size + "K");
	}

	private void clearMap() {
		savePoint();
		canvas.getContext2d().clearRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
		setImageSize();
		clearHandles();
	}

	private void fillMap() {
		savePoint();
		canvas.getContext2d().fillRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
		setImageSize();
		clearHandles();
	}

	private HasValue<Boolean> shaperButton = null;
	private Shaper shaper = pencilShaper;

	@SuppressWarnings("unchecked")
	private void toggleShaper(ClickEvent e, Shaper s) {
		if (e.getSource() instanceof HasValue<?>) {
			HasValue<Boolean> v = (HasValue<Boolean>)e.getSource();
			if (!v.getValue()) {
				v.setValue(true, false);
				return;
			}
			if (shaperButton == null) {
				pencilButton.setValue(false, false);
			} else {
				shaperButton.setValue(false, false);
			}
			shaperButton = v;
			shaper = s;
		}
		erasing = false;
	}

	private Stack<ImageData> undo = new Stack<ImageData>();
	private Stack<ImageData> redo = new Stack<ImageData>();

	private void savePoint() {
		undo.push(canvas.getContext2d().getImageData(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight()));
		redo.clear();
	}

	private void saveRedo() {
		redo.push(canvas.getContext2d().getImageData(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight()));
	}

	private void undoMap() {
		clearHandles();
		if (hasUndoPoints()) {
			saveRedo();
			ImageData data = undo.pop();
			canvas.getContext2d().clearRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
			canvas.getContext2d().putImageData(data, 0, 0);
		}
	}

	private void redoMap() {
		clearHandles();
		if (hasRedoPoints()) {
			ImageData data = redo.pop();
			undo.push(canvas.getContext2d().getImageData(0, 0, canvas.getCoordinateSpaceHeight(), canvas.getCoordinateSpaceHeight()));
			canvas.getContext2d().putImageData(data, 0, 0);
		}
	}

	private boolean hasUndoPoints() {
		return !undo.isEmpty();
	}

	private boolean hasRedoPoints() {
		return !redo.isEmpty();
	}

	private ImageData imageData;

	private void saveImage() {
		imageData = canvas.getContext2d().getImageData(0.0D, 0.0D, (int)Math.round(canvas.getCoordinateSpaceWidth()), (int)Math.round(canvas.getCoordinateSpaceHeight()));
	}

	private void restoreImage() {
		if (imageData == null) {
			GWT.log("Restored image before saved");
			return;
		}
		canvas.getContext2d().clearRect(0.0D, 0.0D, (int)Math.round(canvas.getCoordinateSpaceWidth()), (int)Math.round(canvas.getCoordinateSpaceHeight()));
		canvas.getContext2d().putImageData(imageData, 0.0D, 0.0D);
	}

	private void styleCanvas(Canvas c) {
		c.getContext2d().setStrokeStyle(canvas.getContext2d().getStrokeStyle());
		c.getContext2d().setFillStyle(canvas.getContext2d().getFillStyle());
		c.getContext2d().setLineWidth(canvas.getContext2d().getLineWidth());
	}

	private class PencilShaper implements Shaper {

		@Override
		public void draw(double x, double y) {

			if (!drawing) {

				canvas.getContext2d().beginPath();
				canvas.getContext2d().moveTo(x, y);

			} else {

				canvas.getContext2d().save();
				canvas.getContext2d().setLineCap(LineCap.ROUND);
				canvas.getContext2d().setLineJoin(LineJoin.ROUND);
				canvas.getContext2d().lineTo(x, y);
				if (erasing) {
					canvas.getContext2d().setLineWidth(1);
					canvas.getContext2d().clip();
					canvas.getContext2d().clearRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
				} else {
					canvas.getContext2d().stroke();
				}
				canvas.getContext2d().restore();

			}

		}

		@Override
		public void stop() {
		}

		@Override
		public void redraw(Handle handle) {
		}

	}

	private class LineShaper implements Shaper {

		private double startx = 0.0D;
		private double starty = 0.0D;
		private double finishx = 0.0D;
		private double finishy = 0.0D;
		private double cpx = 0.0D;
		private double cpy = 0.0D;

		@Override
		public void draw(double x, double y) {

			if (!drawing) {

				if (handles.size() == 0) {

					startx = x;
					starty = y;

					saveImage();

				}

			}

			if (handles.size() > 0) {

				redraw(null);

			} else {

				restoreImage();

			}

			finishx = x;
			finishy = y;

			canvas.getContext2d().beginPath();
			canvas.getContext2d().moveTo(startx, starty);
			if (control) {
				cpx = startx + ((finishx - startx) / 2);
				cpy = starty + ((finishy - starty) / 2);
				canvas.getContext2d().quadraticCurveTo(cpx, cpy, finishx, finishy);
			} else {
				cpx = 0.0D;
				cpy = 0.0D;
				canvas.getContext2d().lineTo(finishx, finishy);
			}
			canvas.getContext2d().save();
			if (erasing) {
				canvas.getContext2d().setLineWidth(1);
				canvas.getContext2d().clip();
				canvas.getContext2d().clearRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
			} else {
				canvas.getContext2d().stroke();
			}
			canvas.getContext2d().restore();

		}

		@Override
		public void stop() {

			if (handles.size() == 0) {

				Handle start = new Handle(Handle.Type.Move, handlePanel, startx, starty, LineShaper.this);
				canvas.addMouseMoveHandler(start);
				handles.add(start);
				curverHandles.add(false);

			}

			if (control) {

				Handle cpnt = new Handle(Handle.Type.Move, handlePanel, cpx, cpy, LineShaper.this);
				canvas.addMouseMoveHandler(cpnt);
				handles.add(cpnt);
				curverHandles.add(true);

			}

			Handle finish = new Handle(Handle.Type.Move, handlePanel, finishx, finishy, LineShaper.this);
			canvas.addMouseMoveHandler(finish);
			curverHandles.add(false);
			handles.add(finish);

			redraw(null);

			startx = finishx;
			starty = finishy;

			clientFactory.getEventBus().fireEvent(new MessageEvent(Level.INFO, SHIFT_MESSAGE));

		}

		@Override
		public void redraw(Handle handle) {

			restoreImage();

			canvas.getContext2d().beginPath();

			for (int i = 0; i < handles.size(); i++) {

				if (i == 0) {
					canvas.getContext2d().moveTo(handles.get(i).getX(), handles.get(i).getY());
				} else if (curverHandles.get(i)) {
					double cx = handles.get(i).getX();
					double cy = handles.get(i).getY();
					i++;
					canvas.getContext2d().quadraticCurveTo(cx, cy, handles.get(i).getX(), handles.get(i).getY());
				} else {
					canvas.getContext2d().lineTo(handles.get(i).getX(), handles.get(i).getY());
				}

			}
			canvas.getContext2d().save();
			if (erasing) {
				canvas.getContext2d().setLineWidth(1);
				canvas.getContext2d().clip();
				canvas.getContext2d().clearRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
			} else {
				canvas.getContext2d().stroke();
			}
			canvas.getContext2d().restore();

		}

	}

	private class PolyShaper implements Shaper {

		private double startx = 0.0D;
		private double starty = 0.0D;
		private double finishx = 0.0D;
		private double finishy = 0.0D;
		private double cpx = 0.0D;
		private double cpy = 0.0D;

		@Override
		public void draw(double x, double y) {

			if (!drawing) {

				if (handles.size() == 0) {

					startx = x;
					starty = y;

					saveImage();

				}

			}

			if (handles.size() > 0) {

				redraw(null);

			} else {

				restoreImage();

			}

			finishx = x;
			finishy = y;

			canvas.getContext2d().beginPath();
			canvas.getContext2d().moveTo(startx, starty);
			if (control) {
				cpx = startx + ((finishx - startx) / 2);
				cpy = starty + ((finishy - starty) / 2);
				canvas.getContext2d().quadraticCurveTo(cpx, cpy, finishx, finishy);
			} else {
				cpx = 0.0D;
				cpy = 0.0D;
				canvas.getContext2d().lineTo(finishx, finishy);
			}
			canvas.getContext2d().closePath();
			canvas.getContext2d().save();
			if (erasing) {
				canvas.getContext2d().setLineWidth(1);
				canvas.getContext2d().clip();
				canvas.getContext2d().clearRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
			} else {
				canvas.getContext2d().stroke();
			}
			canvas.getContext2d().restore();

		}

		@Override
		public void stop() {

			if (handles.size() == 0) {

				Handle start = new Handle(Handle.Type.Move, handlePanel, startx, starty, PolyShaper.this);
				canvas.addMouseMoveHandler(start);
				handles.add(start);
				curverHandles.add(false);

			}

			if (control) {

				Handle cpnt = new Handle(Handle.Type.Move, handlePanel, cpx, cpy, PolyShaper.this);
				canvas.addMouseMoveHandler(cpnt);
				handles.add(cpnt);
				curverHandles.add(true);

			}

			Handle finish = new Handle(Handle.Type.Move, handlePanel, finishx, finishy, PolyShaper.this);
			canvas.addMouseMoveHandler(finish);
			handles.add(finish);
			curverHandles.add(false);

			redraw(null);

			startx = finishx;
			starty = finishy;

			clientFactory.getEventBus().fireEvent(new MessageEvent(Level.INFO, SHIFT_MESSAGE));

		}

		@Override
		public void redraw(Handle handle) {

			restoreImage();

			canvas.getContext2d().beginPath();

			for (int i = 0; i < handles.size(); i++) {

				if (i == 0) {
					canvas.getContext2d().moveTo(handles.get(i).getX(), handles.get(i).getY());
				} else if (curverHandles.get(i)) {
					double cx = handles.get(i).getX();
					double cy = handles.get(i).getY();
					i++;
					canvas.getContext2d().quadraticCurveTo(cx, cy, handles.get(i).getX(), handles.get(i).getY());
				} else {
					canvas.getContext2d().lineTo(handles.get(i).getX(), handles.get(i).getY());
				}

			}
			canvas.getContext2d().closePath();
			canvas.getContext2d().save();
			if (erasing) {
				canvas.getContext2d().setLineWidth(1);
				canvas.getContext2d().clip();
				canvas.getContext2d().clearRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
			} else {
				canvas.getContext2d().stroke();
			}
			canvas.getContext2d().restore();

		}

	}

	private class FilledPolyShaper implements Shaper {

		private double startx = 0.0D;
		private double starty = 0.0D;
		private double finishx = 0.0D;
		private double finishy = 0.0D;
		private double cpx = 0.0D;
		private double cpy = 0.0D;

		@Override
		public void draw(double x, double y) {

			if (!drawing) {

				if (handles.size() == 0) {

					startx = x;
					starty = y;

					saveImage();

				}

			}

			if (handles.size() > 0) {

				redraw(null);

			} else {

				restoreImage();

			}

			finishx = x;
			finishy = y;

			canvas.getContext2d().beginPath();
			canvas.getContext2d().moveTo(startx, starty);
			if (control) {
				cpx = startx + ((finishx - startx) / 2);
				cpy = starty + ((finishy - starty) / 2);
				canvas.getContext2d().quadraticCurveTo(cpx, cpy, finishx, finishy);
			} else {
				cpx = 0.0D;
				cpy = 0.0D;
				canvas.getContext2d().lineTo(finishx, finishy);
			}
			canvas.getContext2d().closePath();
			if (erasing) {
				canvas.getContext2d().save();
				canvas.getContext2d().clip();
				canvas.getContext2d().clearRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
				canvas.getContext2d().restore();
			} else {
				canvas.getContext2d().fill();
				if (Integer.parseInt(strokeSizeField.getValue()) > 0) {
					canvas.getContext2d().stroke();
				}
			}

		}

		@Override
		public void stop() {

			if (handles.size() == 0) {

				Handle start = new Handle(Handle.Type.Move, handlePanel, startx, starty, FilledPolyShaper.this);
				canvas.addMouseMoveHandler(start);
				handles.add(start);
				curverHandles.add(false);

			}

			if (control) {

				Handle cpnt = new Handle(Handle.Type.Move, handlePanel, cpx, cpy, FilledPolyShaper.this);
				canvas.addMouseMoveHandler(cpnt);
				handles.add(cpnt);
				curverHandles.add(true);

			}

			Handle finish = new Handle(Handle.Type.Move, handlePanel, finishx, finishy, FilledPolyShaper.this);
			canvas.addMouseMoveHandler(finish);
			handles.add(finish);
			curverHandles.add(false);

			redraw(null);

			startx = finishx;
			starty = finishy;

			clientFactory.getEventBus().fireEvent(new MessageEvent(Level.INFO, SHIFT_MESSAGE));

		}

		@Override
		public void redraw(Handle handle) {

			restoreImage();

			canvas.getContext2d().beginPath();

			for (int i = 0; i < handles.size(); i++) {

				if (i == 0) {
					canvas.getContext2d().moveTo(handles.get(i).getX(), handles.get(i).getY());
				} else if (curverHandles.get(i)) {
					double cx = handles.get(i).getX();
					double cy = handles.get(i).getY();
					i++;
					canvas.getContext2d().quadraticCurveTo(cx, cy, handles.get(i).getX(), handles.get(i).getY());
				} else {
					canvas.getContext2d().lineTo(handles.get(i).getX(), handles.get(i).getY());
				}

			}
			canvas.getContext2d().closePath();

			if (erasing) {
				canvas.getContext2d().save();
				canvas.getContext2d().clip();
				canvas.getContext2d().clearRect(0, 0, canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
				canvas.getContext2d().restore();
			} else {
				canvas.getContext2d().fill();
				if (Integer.parseInt(strokeSizeField.getValue()) > 0) {
					canvas.getContext2d().stroke();
				}
			}

		}

	}

	private class TextShaper implements Shaper {

		private Handle mover;
		private Handle rotater;
		private double moverX = 0.0D;
		private double moverY = 0.0D;

		private double rotaterX = 0.0D;
		private double rotaterY = 0.0D;

		private double rotation;

		@Override
		public void draw(double x, double y) {

			if (text == null || text.length() <= 0) {
				return;
			}
			if (!drawing) {

				saveImage();

				draw(null, x, y);

			}

		}

		private void draw(Handle handle, double startX, double startY) {

			restoreImage();

			Canvas transformCanvas = Canvas.createIfSupported();
			transformCanvas.getContext2d().setFont("14pt Calibri");
			transformCanvas.getContext2d().setTextAlign(TextAlign.CENTER);
			transformCanvas.getContext2d().setTextBaseline(TextBaseline.MIDDLE);
			TextMetrics m = transformCanvas.getContext2d().measureText(text);
			double width = m.getWidth();
			double height = width;
			moverX = startX;
			moverY = startY;
			rotaterX = startX;
			rotaterY = startY - 50;

			//rotate and draw image in new position.
			int imagBoxsize = (int)(Math.sqrt(Math.pow(width, 2.0D) + Math.pow(height, 2.0D)) + 24.0D);

			transformCanvas.setCoordinateSpaceWidth(imagBoxsize);
			transformCanvas.setCoordinateSpaceHeight(imagBoxsize);
			transformCanvas.getContext2d().setFont("12pt Calibri");
			transformCanvas.getContext2d().setFillStyle(textStyleField.getValue());
			transformCanvas.getContext2d().setTextAlign(TextAlign.CENTER);
			transformCanvas.getContext2d().setTextBaseline(TextBaseline.MIDDLE);

			transformCanvas.getContext2d().translate(imagBoxsize / 2, imagBoxsize / 2);
			if (handle != null && handle == rotater) {
				rotation = Math.min(rotaterX - handle.getX(), ROTATION_DRAG_LENGTH) / ROTATION_DRAG_LENGTH;
				transformCanvas.getContext2d().rotate(-rotation * MAX_RADIANS);
			}
			transformCanvas.getContext2d().fillText(text, 0, 0);

			double tlx = startX - (imagBoxsize / 2);
			double tly = startY - (imagBoxsize / 2);
			canvas.getContext2d().drawImage(transformCanvas.getCanvasElement(), tlx, tly, imagBoxsize, imagBoxsize);
		}

		@Override
		public void stop() {

			mover = new Handle(Handle.Type.Move, handlePanel, moverX, moverY, TextShaper.this);
			canvas.addMouseMoveHandler(mover);
			canvas.addMouseUpHandler(mover);
			canvas.addMouseOutHandler(mover);
			handles.add(mover);

			rotater = new Handle(Handle.Type.Rotate, handlePanel, rotaterX, rotaterY, TextShaper.this);
			canvas.addMouseMoveHandler(rotater);
			canvas.addMouseUpHandler(rotater);
			canvas.addMouseOutHandler(rotater);
			handles.add(rotater);

			mover.addMouseMoveHandler(rotater);
			rotater.addMouseMoveHandler(mover);

		}

		@Override
		public void redraw(Handle handle) {

			double offsetX = 0.0D;
			double offsetY = 0.0D;
			if (mover != null) {
				offsetX = mover.getX() - moverX;
				offsetY = mover.getY() - moverY;
			}

			//work out new top left and bottom right corners, allowing for movement of topleft
			//and re-sizing of bottom right.
			double newtopleftX = moverX + offsetX;
			double newtopleftY = moverY + offsetY;
			double newbottomrightX = rotaterX + offsetX;
			double newbottomrightY = rotaterY + offsetY;

			//re-draw
			draw(handle, newtopleftX, newtopleftY);

			//adjust position of handles around newly positioned and sized image
			mover.setPosition(newtopleftX, newtopleftY);
			moverX = mover.getX();
			moverY = mover.getY();
			rotater.setPosition(newbottomrightX, newbottomrightY);
			mover.setPosition(newtopleftX, newtopleftY);

		}

	}

	private class ImageShaper implements Shaper {

		private Handle topleft;
		private Handle bottomright;
		private Handle rotater;
		private Handle mover;
		private double moverX = 0.0D;
		private double moverY = 0.0D;

		private double startX = 0.0D;
		private double startY = 0.0D;
		private double finishX = 0.0D;
		private double finishY = 0.0D;

		private double rotation;
		private double width;
		private double height;

		@Override
		public void draw(double x, double y) {

			if (image == null) {
				return;
			}
			if (!drawing) {

				saveImage();

			} else {

				startX = x;
				startY = y;
				finishX = x + width;
				finishY = y + height;

				draw(null, startX, startY, finishX, finishY);

			}

		}

		private void draw(Handle handle, double newtopleftX, double newtopleftY, double newbottomrightX, double newbottomrightY) {

			restoreImage();

			double width = Math.abs(newbottomrightX - newtopleftX);
			double height = Math.abs(newbottomrightY - newtopleftY);

			//rotate and draw image in new position.
			int imagBoxsize = (int)Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)) + 24;

			Canvas transformCanvas = Canvas.createIfSupported();
			transformCanvas.setCoordinateSpaceWidth(imagBoxsize);
			transformCanvas.setCoordinateSpaceHeight(imagBoxsize);

			transformCanvas.getContext2d().translate(imagBoxsize / 2, imagBoxsize / 2);
			if (handle != null && handle == rotater) {
				rotation = Math.min(bottomright.getX() - handle.getX(), ROTATION_DRAG_LENGTH) / ROTATION_DRAG_LENGTH;
				transformCanvas.getContext2d().rotate(-rotation * MAX_RADIANS);
			}
			styleCanvas(transformCanvas);
			transformCanvas.getContext2d().drawImage(image, -width / 2, -height / 2, width, height);

			double tlx = Math.min(newtopleftX, newbottomrightX) - ((imagBoxsize - width) / 2);
			double tly = Math.min(newtopleftY, newbottomrightY) - ((imagBoxsize - height) / 2);
			canvas.getContext2d().drawImage(transformCanvas.getCanvasElement(), tlx, tly, imagBoxsize, imagBoxsize);

		}

		@Override
		public void stop() {

			if (image == null) {
				clientFactory.getEventBus().fireEvent(new MessageEvent(Level.INFO, "Load an image first"));
				return;
			}
			moverX = startX;
			moverY = finishY;

			mover = new Handle(Handle.Type.Move, handlePanel, startX, finishY, ImageShaper.this);
			canvas.addMouseMoveHandler(mover);
			canvas.addMouseUpHandler(mover);
			canvas.addMouseOutHandler(mover);
			handles.add(mover);

			rotater = new Handle(Handle.Type.Rotate, handlePanel, finishX, startY, ImageShaper.this);
			canvas.addMouseMoveHandler(rotater);
			canvas.addMouseUpHandler(rotater);
			canvas.addMouseOutHandler(rotater);
			handles.add(rotater);

			topleft = new Handle(Handle.Type.Size, handlePanel, startX, startY, ImageShaper.this);
			canvas.addMouseMoveHandler(topleft);
			canvas.addMouseUpHandler(topleft);
			canvas.addMouseOutHandler(topleft);
			handles.add(topleft);

			bottomright = new Handle(Handle.Type.Size, handlePanel, finishX, finishY, ImageShaper.this);
			canvas.addMouseMoveHandler(bottomright);
			canvas.addMouseUpHandler(bottomright);
			canvas.addMouseOutHandler(bottomright);
			handles.add(bottomright);

			topleft.addMouseMoveHandler(bottomright);
			topleft.addMouseMoveHandler(mover);
			topleft.addMouseMoveHandler(rotater);
			bottomright.addMouseMoveHandler(topleft);
			bottomright.addMouseMoveHandler(mover);
			bottomright.addMouseMoveHandler(rotater);
			mover.addMouseMoveHandler(bottomright);
			mover.addMouseMoveHandler(topleft);
			mover.addMouseMoveHandler(rotater);
			rotater.addMouseMoveHandler(topleft);
			rotater.addMouseMoveHandler(bottomright);
			rotater.addMouseMoveHandler(mover);

		}

		@Override
		public void redraw(Handle handle) {

			double offsetX = 0.0D;
			double offsetY = 0.0D;
			if (mover != null) {
				offsetX = mover.getX() - moverX;
				offsetY = mover.getY() - moverY;
			}

			//work out new top left and bottom right corners, allowing for movement of topleft
			//and re-sizing of bottom right.
			double newtopleftX = topleft.getX() + offsetX;
			double newtopleftY = topleft.getY() + offsetY;
			double newbottomrightX = bottomright.getX() + offsetX;
			double newbottomrightY = bottomright.getY() + offsetY;

			width = Math.abs(newbottomrightX - newtopleftX);
			height = Math.abs(newbottomrightY - newtopleftY);

			//re-draw
			draw(handle, newtopleftX, newtopleftY, newbottomrightX, newbottomrightY);

			//adjust position of handles around newly positioned and sized image
			mover.setPosition(newtopleftX, newbottomrightY);
			moverX = mover.getX();
			moverY = mover.getY();
			rotater.setPosition(newbottomrightX, newtopleftY);
			if (handle != bottomright) {
				topleft.setPosition(newtopleftX, newtopleftY);
			}
			if (handle != topleft) {
				bottomright.setPosition(newbottomrightX, newbottomrightY);
			}

		}

	}

}


text:
	#define CONTACT_OFFSET			0.01f
//	#define CONTACT_OFFSET			0.1f
//	#define STEP_OFFSET				0.01f
	#define STEP_OFFSET				0.05f
//	#define STEP_OFFSET				0.1f
//	#define STEP_OFFSET				0.2f

//	#define SLOPE_LIMIT				0.8f
	#define SLOPE_LIMIT				0.0f
//	#define INVISIBLE_WALLS_HEIGHT	6.0f
	#define INVISIBLE_WALLS_HEIGHT	0.0f
//	#define MAX_JUMP_HEIGHT			4.0f
	#define MAX_JUMP_HEIGHT			0.0f

	static const float gScaleFactor			= 1.5f;
	static const float gStandingSize		= 1.0f * gScaleFactor;
	static const float gCrouchingSize		= 0.25f * gScaleFactor;
	static const float gControllerRadius	= 0.3f * gScaleFactor;

	const char* gPlankName		= "Plank";
	const char* gPlatformName	= "Platform";

#endif

using namespace SampleRenderer;
using namespace SampleFramework;

REGISTER_SAMPLE(SampleBridges, "SampleBridges")

//using namespace physx;

static const bool gBreakableBridges = false;
static const PxReal gBreakForce = 1.0f;
//static const PxReal gPlatformSpeed = 100.0f;
static const PxReal gPlatformSpeed = 10.0f;
//static const PxReal gPlatformSpeed = 5.0f;
static const PxReal gGlobalScale = 2.0f;

static const PxReal gBoxUVs[] =
{
	0.0f, 180.0f/256.0f,
	0.0f, 0.0f,
	1.0f, 0.0f,
	1.0f, 180.0f/256.0f,

	0.0f, 180.0f/256.0f,
	0.0f, 0.0f,
	1.0f, 0.0f,
	1.0f, 180.0f/256.0f,

	0.0f, 180.0f/256.0f,
	0.0f, 0.0f,
	1.0f, 0.0f,
	1.0f, 180.0f/256.0f,

	0.0f, 180.0f/256.0f,
	0.0f, 0.0f,
	1.0f, 0.0f,
	1.0f, 180.0f/256.0f,

	0.0f, 180.0f/256.0f,
	0.0f, 0.0f,
	1.0f, 0.0f,
	1.0f, 180.0f/256.0f,

	0.0f, 180.0f/256.0f,
	0.0f, 0.0f,
	1.0f, 0.0f,
	1.0f, 180.0f/256.0f,
};

///////////////////////////////////////////////////////////////////////////////

// PT: TODO: use PhysXSampleApplication helper for this

static PxRigidActor* createRigidActor(	PxScene& scene, PxPhysics& physics,
										const PxTransform& pose, const PxGeometry& geometry, PxMaterial& material,
										const PxFilterData* fd, const PxReal* density, const PxReal* mass, PxU32 flags)
{
	const bool isDynamic = (density && *density) || (mass && *mass);

	PxRigidActor* actor = isDynamic ? static_cast<PxRigidActor*>(physics.createRigidDynamic(pose))
									: static_cast<PxRigidActor*>(physics.createRigidStatic(pose));
	if(!actor)
		return NULL;

	PxShape* shape = actor->createShape(geometry, material);
	if(!shape)
	{
		actor->release();
		return NULL;
	}

	if(fd)
		shape->setSimulationFilterData(*fd);

	if(isDynamic)
	{
		PxRigidDynamic* body = static_cast<PxRigidDynamic*>(actor);
		{
			if(density)
				PxRigidBodyExt::updateMassAndInertia(*body, *density);
			else
				PxRigidBodyExt::setMassAndUpdateInertia(*body, *mass);
		}
	}

	scene.addActor(*actor);

	return actor;
}

static PxRigidActor* createBox(	PxScene& scene, PxPhysics& physics,
								const PxTransform& pose, const PxVec3& dimensions, const PxReal density, PxMaterial& m, const PxFilterData* fd, PxU32 flags)
{
	return createRigidActor(scene, physics, pose, PxBoxGeometry(dimensions), m, fd, &density, NULL, flags);
}

enum BridgeJoint
{
	BRIDGE_REVOLUTE,
	BRIDGE_SPHERICAL,
	BRIDGE_FIXED,

	BRIDGE_COUNT
};

// PT: TODO: share this function with original code in Visual Tests?
static void buildBridge(PxScene& scene, PxPhysics& physics, PxMaterial& material, std::vector<PxRigidActor*>& actors, std::vector<PxJoint*>& joints, const PxTransform& tr, PxReal scale, BridgeJoint type)
{
	const PxVec3 offset(0,0,0);

	const PxReal groundLength = 5.0f;
	const PxReal groundHeight = 5.0f;

	// create ground under character
	if(1)
	{
		PxTransform	boxPose = tr * PxTransform(PxVec3(0.0f, -groundHeight, -10.0f-groundLength)*scale, PxQuat(PxIdentity));
		boxPose.p += offset;

		const PxVec3 boxSize = PxVec3(1.0f, groundHeight, groundLength) * scale;
		PxRigidActor* box = createBox(scene, physics, boxPose, boxSize, 0.0f, material, NULL, 0);
		actors.push_back(box);
	}

	// create bridge...
	if(1)
	{
		const PxReal plankWidth		= 1.0f;
		const PxReal plankHeight	= 0.02f;
		const PxReal plankDepth		= 0.5f;
		const PxReal plankDensity	= 0.03f;
//		const PxReal plankDensity	= 0.3f;
		PxRigidDynamic* lastPlank	= 0;

		for(PxReal z = -10.0f+plankDepth; z<10.0f; z+= plankDepth*2)
		{
			const PxVec3 boxSize = PxVec3(plankWidth, plankHeight, plankDepth) * scale;

			PxTransform boxPose = tr * PxTransform(PxVec3(0,-plankHeight,z)*scale);

			PxRigidDynamic* plank = createBox(scene, physics, boxPose, boxSize, plankDensity, material, NULL, 0)->is<PxRigidDynamic>();
			if(plank)
			{
#ifdef CCT_ON_BRIDGES
				plank->setName(gPlankName);
#endif
				actors.push_back(plank);

				//plank->setSolverIterationCounts(1, 1);
//				plank->setSolverIterationCounts(255, 255);
//				plank->setSolverIterationCounts(10, 10);
				plank->setSolverIterationCounts(8, 8);
				plank->setLinearDamping(1.0f);
				plank->setAngularDamping(1.0f);
//				plank->putToSleep();

				if(!lastPlank)
				{
					// plank = first plank...
					plank->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true);
				}
				else
				{
					// joint together plank and lastPlank...
					if(type==BRIDGE_REVOLUTE)
					{
						PxRevoluteJoint* j = PxRevoluteJointCreate(physics,
																   lastPlank, PxTransform(PxVec3(0,0, plankDepth)*scale),
																   plank, PxTransform(PxVec3(0,0,-plankDepth)*scale));
						if(j)
						{
							j->setProjectionLinearTolerance(0.5f);
							joints.push_back(j);
						}
					}
					else if(type==BRIDGE_FIXED)
					{
						PxFixedJoint* j = PxFixedJointCreate(physics,
															 lastPlank, PxTransform(PxVec3(0,0, plankDepth)*scale),
															 plank, PxTransform(PxVec3(0,0,-plankDepth)*scale));
						if(j)
						{
							j->setProjectionLinearTolerance(0.5f);
							joints.push_back(j);
						}
					}
					else if(type==BRIDGE_SPHERICAL)
					{
						const PxReal plankX = 0.1f;
						PxSphericalJoint* j1 = PxSphericalJointCreate(physics,
															          lastPlank, PxTransform(PxVec3(plankX, 0.0f, plankDepth)*scale),
															          plank, PxTransform(PxVec3(plankX, 0.0f, -plankDepth)*scale));
						if(j1)
						{
							j1->setProjectionLinearTolerance(0.5f);
							joints.push_back(j1);
						}

						PxSphericalJoint* j2 = PxSphericalJointCreate(physics,
															          lastPlank, PxTransform(PxVec3(-plankX, 0.0f, plankDepth)*scale),
															          plank, PxTransform(PxVec3(-plankX, 0.0f, -plankDepth)*scale));
						if(j2)
						{
							j2->setProjectionLinearTolerance(0.5f);
							joints.push_back(j2);
						}
					}
					else PX_ASSERT(0);
				}
			}

			if(gBreakableBridges)
			{
				for(PxU32 i=0;i<joints.size();i++)
					joints[i]->setBreakForce(gBreakForce * scale, gBreakForce * scale);
			}

			lastPlank = plank;
		}
		if(lastPlank)
		{
			lastPlank->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true);
		}
	}

	// create ground after bridge
	if(1)
	{
		PxTransform	boxPose = tr * PxTransform(PxVec3(0.0f, -groundHeight, 10.0f+groundLength)*scale);
		boxPose.p += offset;

		const PxVec3 boxSize = PxVec3(1.0f, groundHeight, groundLength) * scale;
		PxRigidActor* box = createBox(scene, physics, boxPose, boxSize, 0.0f, material, NULL, 0);
		actors.push_back(box);
	}
}

///////////////////////////////////////////////////////////////////////////////

SampleBridges::SampleBridges(PhysXSampleApplication& app) :
	PhysXSample				(app),
#ifdef CCT_ON_BRIDGES
	mCCTCamera				(NULL),
	mActor					(NULL),
	mControllerManager		(NULL),
#endif
#ifdef PLATFORMS_AS_OBSTACLES
	mObstacleContext		(NULL),
	mBoxObstacles			(NULL),
#endif
	mGlobalScale			(gGlobalScale),
	mCurrentPlatform		(0xffffffff),
	mElapsedRenderTime		(0.0f),
	mRockMaterial			(NULL),
	mWoodMaterial			(NULL),
	mPlatformMaterial		(NULL),
	mBomb					(NULL),
	mBombTimer				(0.0f),
	mWaterY					(0.0f),
	mMustResync				(false),
	mEnableCCTDebugRender	(false)
{
#ifdef CCT_ON_BRIDGES
	mControllerInitialPosition = PxExtendedVec3(18.0f, 2.0f, -64.0f);
#endif
	mCreateGroundPlane	= false;
	//mStepperType = FIXED_STEPPER;
	mDefaultDensity		= 0.1f;
//	mDefaultDensity		= 1.0f;

}

SampleBridges::~SampleBridges()
{
#ifdef CCT_ON_BRIDGES
	DELETESINGLE(mActor);
#endif
}

///////////////////////////////////////////////////////////////////////////////

void SampleBridges::customizeSample(SampleSetup& setup)
{
	setup.mName	= "SampleBridges";
}

///////////////////////////////////////////////////////////////////////////////

// PT: this is called from the RAW-loader when loading a scene. The default behavior
// in PhysXSampleApplication creates a physics and a graphics mesh for each exported object. We
// intercept the call here to disable the physics mesh for the "water plane".
void SampleBridges::newMesh(const RAWMesh& data)
{
	if(data.mName && (strcmp(data.mName, "WaterPlane")==0 || strcmp(data.mName, "skyshape")==0))
	{
		// PT: create render mesh only (not physics) for the waterplane & skyboxes
		createRenderMeshFromRawMesh(data);
		if(strcmp(data.mName, "WaterPlane")==0)
			mWaterY = data.mTransform.p.y;
		return;
	}
	PhysXSample::newMesh(data);
}

// PT: this is a callback from the RAW-loader, used to import a new shape. We use
// simple segment shapes in the MAX scene to define where our bridges should be.
void SampleBridges::newShape(const RAWShape& data)
{
	if(data.mName)
	{
		if(strcmp(data.mName, "Bridge")==0)
		{
			PX_ASSERT(data.mNbVerts==2);
			const PxVec3 p0 = data.mTransform.transform(data.mVerts[0]);
			const PxVec3 p1 = data.mTransform.transform(data.mVerts[1]);
			const PxVec3 center = (p0 + p1)*0.5f;

			PxVec3 dir, right, up;
			Ps::computeBasis(p0, p1, dir, right, up);

			PxMat33 m(right, up, dir);
			PxTransform tr;
			tr.p = center;
			tr.q = PxQuat(m);

			const float l = (p1 - p0).magnitude();
			const float coeff = l / 80.0f;

			float scale = 2.0f * coeff;
	//		float scale = 4.0f * coeff;
	//		float scale = 1.0f;

			BridgeJoint type = BRIDGE_REVOLUTE;
			{
				static int count=0;
				if(count==0)
					type = BRIDGE_REVOLUTE;
				else if(count==1)
					type = BRIDGE_SPHERICAL;
				else if(count==2)
					type = BRIDGE_FIXED;
				else PX_ASSERT(0);

				count++;
				if(count==BRIDGE_COUNT)
					count = 0;
			}

			std::vector<PxRigidActor*> bridgeActors;
			buildBridge(getActiveScene(), getPhysics(), getDefaultMaterial(), bridgeActors, mJoints, tr, scale, type);

			for(PxU32 i=0;i<bridgeActors.size();i++)
			{
				RenderMaterial* m = (i==0 || i==bridgeActors.size()-1) ? mRockMaterial : mWoodMaterial;

				PX_ASSERT(bridgeActors[i]->getNbShapes()==1);
				PxShape* boxShape;
				PxU32 nb = bridgeActors[i]->getShapes(&boxShape, 1);
				PX_ASSERT(nb==1);
				PX_UNUSED(nb);
				createRenderBoxFromShape(bridgeActors[i], boxShape, m, gBoxUVs);
			}
		}
		else if(strcmp(data.mName, "Platform")==0)
		{
			const PxTransform idt = PxTransform(PxIdentity);

			PxRigidDynamic* platformActor = (PxRigidDynamic*)::createBox(getActiveScene(), getPhysics(), idt, gGlobalScale * PxVec3(1.0f, 5.0f, 5.0f), 1.0f, getDefaultMaterial(), NULL, 0);
#ifdef CCT_ON_BRIDGES
			platformActor->setName(gPlatformName);
#endif
			platformActor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true);

//			createRenderObjectsFromActor(platformActor, mPlatformMaterial);
			PxShape* platformShape = NULL;
			platformActor->getShapes(&platformShape, 1);
			PX_ASSERT(platformShape);
			RenderBaseActor* renderActor = createRenderObjectFromShape(platformActor, platformShape, mPlatformMaterial);

#ifdef PLATFORMS_AS_OBSTACLES
//			platformShape->userData = NULL;
			renderActor->setPhysicsShape(NULL, NULL);
			PxBounds3 maxBounds;
			maxBounds.setMaximal();
			renderActor->setWorldBounds(maxBounds);
#else
			PX_UNUSED(renderActor);
#endif

#ifdef ROTATING_PLATFORMS
			const PxF32 rotationSpeed = 0.5f;
#else
			const PxF32 rotationSpeed = 0.0f;
#endif
			const PxVec3 up(0.0f, 1.0f, 0.0f);
			const PxQuat q = Ps::rotationArc(PxVec3(1.0f, 0.0f, 0.0f), up);

			mPlatformManager.createPlatform(data.mNbVerts, data.mVerts, data.mTransform, q, platformActor, gPlatformSpeed, rotationSpeed);
		}
	}
}

///////////////////////////////////////////////////////////////////////////////

void SampleBridges::onInit()
{
	PhysXSample::onInit();
	PxSceneWriteLock scopedLock(*mScene);

	mApplication.setMouseCursorHiding(true);
	mApplication.setMouseCursorRecentering(true);

	getRenderer()->setAmbientColor(RendererColor(170, 170, 170));
//	mScene->setGravity(PxVec3(0.0f, -98.1f, 0.0f));
	getActiveScene().setGravity(PxVec3(0.0f, -9.81f, 0.0f));

	{
		{
			RAWTexture data;
			data.mName = "rock_distance_diffuse.tga";
			RenderTexture* rockTexture = createRenderTextureFromRawTexture(data);

			mRockMaterial = SAMPLE_NEW(RenderMaterial)(*getRenderer(), PxVec3(1.0f, 1.0f, 1.0f), 1.0f, false, 0xffffffff, rockTexture);
			mRenderMaterials.push_back(mRockMaterial);
		}

		{
			RAWTexture data;
			data.mName = "pier_diffuse.dds";
			RenderTexture* woodTexture = createRenderTextureFromRawTexture(data);

			mWoodMaterial = SAMPLE_NEW(RenderMaterial)(*getRenderer(), PxVec3(1.0f, 1.0f, 1.0f), 1.0f, false, 0xffffffff, woodTexture);
			mRenderMaterials.push_back(mWoodMaterial);
		}

		{
			RAWTexture data;
			data.mName = "divingBoardFloor_diffuse.dds";
			RenderTexture* platformTexture = createRenderTextureFromRawTexture(data);

			mPlatformMaterial = SAMPLE_NEW(RenderMaterial)(*getRenderer(), PxVec3(1.0f, 1.0f, 1.0f), 1.0f, false, 0xffffffff, platformTexture);
			mRenderMaterials.push_back(mPlatformMaterial);
		}

		importRAWFile("SampleBridges.raw", gGlobalScale);
		importRAWFile("sky_mission_race1.raw", 1.0f);
	}

#ifdef CCT_ON_BRIDGES
	mControllerManager = PxCreateControllerManager(getActiveScene());

	ControlledActorDesc desc;
	desc.mType					= PxControllerShapeType::eCAPSULE;
	desc.mPosition				= mControllerInitialPosition;
	desc.mSlopeLimit			= SLOPE_LIMIT;
	desc.mContactOffset			= CONTACT_OFFSET;
	desc.mStepOffset			= STEP_OFFSET;
	desc.mInvisibleWallHeight	= INVISIBLE_WALLS_HEIGHT;
	desc.mMaxJumpHeight			= MAX_JUMP_HEIGHT;
	desc.mRadius				= gControllerRadius;
	desc.mHeight				= gStandingSize;
	desc.mCrouchHeight			= gCrouchingSize;
	desc.mReportCallback		= this;
	desc.mBehaviorCallback		= this;

	{
		mActor = SAMPLE_NEW(ControlledActor)(*this);
		mActor->init(desc, mControllerManager);

		RenderBaseActor* actor0 = mActor->getRenderActorStanding();
		RenderBaseActor* actor1 = mActor->getRenderActorCrouching();
		if(actor0)
			mRenderActors.push_back(actor0);
		if(actor1)
			mRenderActors.push_back(actor1);
	}

	mCCTCamera = SAMPLE_NEW(SampleCCTCameraController)(*this);
	mCCTCamera->setControlled(&mActor, 0, 1);
//	mCCTCamera->setFilterData();
	mCCTCamera->setFilterCallback(this);
	mCCTCamera->setGravity(-20.0f);

	setCameraController(mCCTCamera);

	mCCTCamera->setView(0,0);

	#ifdef PLATFORMS_AS_OBSTACLES
	mObstacleContext = mControllerManager->createObstacleContext();
	mCCTCamera->setObstacleContext(mObstacleContext);

	const PxVec3 up(0.0f, 1.0f, 0.0f);
	const PxQuat q = Ps::rotationArc(PxVec3(1.0f, 0.0f, 0.0f), up);

	const PxU32 nbPlatforms = mPlatformManager.getNbPlatforms();
	KinematicPlatform** platforms = mPlatformManager.getPlatforms();
	mBoxObstacles = new PxBoxObstacle[nbPlatforms];
	for(PxU32 i=0;i<nbPlatforms;i++)
	{
		mBoxObstacles[i].mPos.x = 0.0;
		mBoxObstacles[i].mPos.y = 0.0;
		mBoxObstacles[i].mPos.z = 0.0;
		mBoxObstacles[i].mRot = q;
		mBoxObstacles[i].mHalfExtents = gGlobalScale * PxVec3(1.0f, 5.0f, 5.0f);
//		mBoxObstacles[i].mHalfExtents = gGlobalScale * PxVec3(5.0f, 1.0f, 5.0f);
		const ObstacleHandle handle = mObstacleContext->addObstacle(mBoxObstacles[i]);
		platforms[i]->setBoxObstacle(handle, mBoxObstacles + i);
	}
	#endif

//	mCCTCamera->setCameraLinkToPhysics(true);
#endif

//	mScene->setVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL, 1.0f);
//	mScene->setVisualizationParameter(PxVisualizationParameter::eJOINT_LOCAL_FRAMES, 1.0f);

//	advanceSimulation(4.0f);
}

void SampleBridges::onShutdown()
{
	{
		PxSceneWriteLock scopedLock(*mScene);
#ifdef CCT_ON_BRIDGES
		DELETESINGLE(mCCTCamera);
		#ifdef PLATFORMS_AS_OBSTACLES
		SAFE_RELEASE(mObstacleContext);
		DELETEARRAY(mBoxObstacles);
		#endif
		mControllerManager->release();
#endif
		const size_t nbJoints = mJoints.size();
		for(PxU32 i=0;i<nbJoints;i++)
			if(mJoints[i])
				mJoints[i]->release();
		mJoints.clear();
	}

	mPlatformManager.release();

	PhysXSample::onShutdown();
}

// PT: very crude attempt here, just to have something running
static void applyBombForce(PxRigidActor* actor, const PxVec3& actorPos, const PxVec3& bombPos, PxReal bombRange)
{
	PxRigidDynamic* dyna = actor->is<PxRigidDynamic>();
	if(dyna)
	{
		PxVec3 dir = (actorPos - bombPos);
		const PxReal m = dir.magnitude();
		if(m<bombRange)
		{
			const PxReal coeff = (1.0f - m/bombRange) * 50.0f;
			dir /= m;
//			dyna->addForce(dir*coeff);

			PxBounds3 worldBounds = dyna->getWorldBounds();

			const PxVec3 center = worldBounds.getCenter();
			const PxVec3 extents = worldBounds.getExtents();
			const PxVec3 axis0(extents.x, 0.0f, 0.0f);
			const PxVec3 axis1(0.0f, extents.y, 0.0f);
			const PxVec3 axis2(0.0f, 0.0f, extents.z);

			PxVec3 pts[8];
			pts[0] = pts[3] = pts[4] = pts[7] = center - axis0;
			pts[1] = pts[2] = pts[5] = pts[6] = center + axis0;

			PxVec3 tmp = axis1 + axis2;
			pts[0] -= tmp;
			pts[1] -= tmp;
			pts[6] += tmp;
			pts[7] += tmp;

			tmp = axis1 - axis2;
			pts[2] += tmp;
			pts[3] += tmp;
			pts[4] -= tmp;
			pts[5] -= tmp;

			PxReal minDist = 1e+30f;
			PxU32 index = 0;
			for(PxU32 i=0;i<8;i++)
			{
				PxReal d2 = (pts[i] - bombPos).magnitudeSquared();
				if(d2<minDist)
				{
					minDist = d2;
					index = i;
				}
			}

			PxRigidBodyExt::addForceAtPos(*dyna, dir*coeff, pts[index]);
//			PxRigidBodyExt::addForceAtPos(*dyna, dir*coeff, actorPos);
		}
	}
}

template<class T>
bool findAndReplaceWithLast(std::vector<T*>& array, T* ptr)
{
	const size_t s = array.size();
	for(PxU32 j=0; j<s; j++)
	{
		if(array[j]==ptr)
		{
			T* last = array[s-1];
			array.pop_back();
			if(j!=s-1)
				array[j] = last;
			return true;
		}
	}
	return false;
}

void SampleBridges::onTickPreRender(float dtime)
{
//	shdfnd::printFormatted("SampleBridges::onTickPreRender\n");

	const bool paused = isPaused();

	// PT: keep track of elapsed render time
	if(!paused)
		mElapsedRenderTime += dtime;

#ifdef CCT_ON_BRIDGES
//	const bool postUpdate = mCCTCamera->getCameraLinkToPhysics();
	const bool postUpdate = true;
//	const bool postUpdate = false;

	if(!postUpdate && !mPause)
	{
//		shdfnd::printFormatted("CCT sync\n");
		mActor->sync();
	}
#endif

	if(!paused)
	{
//		mPlatformManager.updatePhysicsPlatforms(dtime);

		if(mBomb)
		{
			mBombTimer -= dtime;
			if(mBombTimer<0.0f)
			{
				PxSceneWriteLock scopedLock(*mScene);
				mBombTimer = 0.0f;

				{
					const PxVec3 bombPos = mBomb->getGlobalPose().p;
					const PxReal bombActorRange = 30.0f;
					const PxReal bombJointRange = 10.0f;
					size_t nbJoints = mJoints.size();
					for(PxU32 i=0;i<nbJoints;i++)
					{
						PxJoint* j = mJoints[i];
						if(!j)
							continue;

						PxRigidActor* actor0;
						PxRigidActor* actor1;
						j->getActors(actor0, actor1);
						const PxVec3 actorPos0 = actor0->getGlobalPose().p;
						const PxVec3 actorPos1 = actor1->getGlobalPose().p;

						applyBombForce(actor0, actorPos0, bombPos, bombActorRange);
						applyBombForce(actor1, actorPos1, bombPos, bombActorRange);

						const PxVec3 jointPos = (actorPos0 + actorPos1)*0.5f;
						const PxReal d2 = (bombPos - jointPos).magnitudeSquared();
						if(d2<bombJointRange*bombJointRange)
						{
							j->release();
							mJoints[i] = NULL;
						}
					}
				}

				{
					PxU32 nbShapes = mBomb->getNbShapes();
					PxShape** shapes = (PxShape**)SAMPLE_ALLOC(sizeof(PxShape*)*nbShapes);
					PxU32 nb = mBomb->getShapes(shapes, nbShapes);
					PX_ASSERT(nb==nbShapes);
					PX_UNUSED(nb);
					for(PxU32 i=0;i<nbShapes;i++)
					{
						PxShape* currentShape = shapes[i];

						RenderBaseActor* renderActor = getRenderActor(mBomb, currentShape);
						unlink(renderActor, currentShape, mBomb);

						findAndReplaceWithLast(mRenderActors, renderActor);

						renderActor->release();
					}
					SAMPLE_FREE(shapes);
				}

				findAndReplaceWithLast(mPhysicsActors, mBomb);

				SAFE_RELEASE(mBomb);
			}
		}
	}

	PhysXSample::onTickPreRender(dtime);

#ifdef PLATFORMS_AS_OBSTACLES
	if(!paused)
		updateRenderPlatforms(dtime);
#endif

#ifdef CCT_ON_BRIDGES
	if(postUpdate && !mPause)
	{
//		shdfnd::printFormatted("CCT sync\n");
		mActor->sync();
	}
#endif
}

///////////////////////////////////////////////////////////////////////////////

void SampleBridges::onTickPostRender(float dtime)
{
	PhysXSample::onTickPostRender(dtime);

	// PT: add CCT's internal debug rendering
	if(mEnableCCTDebugRender)
	{
		mControllerManager->setDebugRenderingFlags(PxControllerDebugRenderFlag::eALL);
		PxRenderBuffer& renderBuffer = mControllerManager->getRenderBuffer();
		RenderPhysX3Debug* renderer = getDebugRenderer();
		renderer->update(renderBuffer);
		renderBuffer.clear();
	}
	else
	{
		mControllerManager->setDebugRenderingFlags(PxControllerDebugRenderFlag::eNONE);
	}

#ifdef CCT_ON_BRIDGES
//	mControllerManager->setDebugRenderingFlags(PxControllerDebugRenderFlag::eCACHED_BV|PxControllerDebugRenderFlag::eTEMPORAL_BV);
	PxRenderBuffer& renderBuffer = mControllerManager->getRenderBuffer();
	getDebugRenderer()->update(renderBuffer);
	renderBuffer.clear();
#endif

	const float lag = fabsf(mPlatformManager.getElapsedTime() - mElapsedRenderTime);
	mMustResync = lag > 1.0f/60.0f;

	if(0)
	{
		shdfnd::printFormatted("Render time:   %f\n", mElapsedRenderTime);
		shdfnd::printFormatted("Platform time: %f\n", mPlatformManager.getElapsedTime());
		shdfnd::printFormatted("Delta:         %f\n", lag);
	}
}

///////////////////////////////////////////////////////////////////////////////

#ifdef PLATFORMS_AS_OBSTACLES
void SampleBridges::updateRenderPlatforms(float dtime)
{
	// PT: compute new positions for (render) platforms, then move their corresponding obstacles to these positions.
	const PxU32 nbPlatforms = mPlatformManager.getNbPlatforms();
	KinematicPlatform** platforms = mPlatformManager.getPlatforms();

	{
		PxSceneReadLock scopedLock(*mScene);

		for(PxU32 i=0;i<nbPlatforms;i++)
		{
			if(1)
			{
				PxRigidDynamic* actor = platforms[i]->getPhysicsActor();
				PxShape* shape = NULL;
				actor->getShapes(&shape, 1);
				RenderBaseActor* renderActor = getRenderActor(actor, shape);
				renderActor->setTransform(platforms[i]->getRenderPose());
			}

			platforms[i]->updateRender(dtime, mObstacleContext);
		}
	}

	// PT: if render time & physics time varies too much, we must resync the physics to avoid large visual mismatchs
	if(mMustResync)
	{
		PxSceneWriteLock scopedLock(*mScene);

//		mElapsedPlatformTime = mElapsedRenderTime;
		mPlatformManager.syncElapsedTime(mElapsedRenderTime);
//		static int count=0;		shdfnd::printFormatted("Resync %d\n", count++);
		for(PxU32 i=0;i<nbPlatforms;i++)
		{
			PxRigidDynamic* actor = platforms[i]->getPhysicsActor();
			actor->setKinematicTarget(platforms[i]->getRenderPose());
			platforms[i]->resync();
		}
	}
}
#endif

///////////////////////////////////////////////////////////////////////////////

void SampleBridges::onSubstep(float dtime)
{
//	shdfnd::printFormatted("onSubstep\n");
	mPlatformManager.updatePhysicsPlatforms(dtime);
}

///////////////////////////////////////////////////////////////////////////////

void SampleBridges::helpRender(PxU32 x, PxU32 y, PxU8 textAlpha)
{
	Renderer* renderer = getRenderer();
	const PxU32 yInc = 18;
	const PxReal scale = 0.5f;
	const PxReal shadowOffset = 6.0f;
	const RendererColor textColor(255, 255, 255, textAlpha);
	const bool isMouseSupported = getApplication().getPlatform()->getSampleUserInput()->mouseSupported();
	const bool isPadSupported = getApplication().getPlatform()->getSampleUserInput()->gamepadSupported();
	const char* msg;

	if (isMouseSupported && isPadSupported)
		renderer->print(x, y += yInc, "Use mouse or right stick to rotate the camera", scale, shadowOffset, textColor);
	else if (isMouseSupported)
		renderer->print(x, y += yInc, "Use mouse to rotate the camera", scale, shadowOffset, textColor);
	else if (isPadSupported)
		renderer->print(x, y += yInc, "Use right stick to rotate the camera", scale, shadowOffset, textColor);
	if (isPadSupported)
		renderer->print(x, y += yInc, "Use left stick to move",scale, shadowOffset, textColor);
	msg = mApplication.inputMoveInfoMsg("Press "," to move", CAMERA_MOVE_FORWARD,CAMERA_MOVE_BACKWARD, CAMERA_MOVE_LEFT, CAMERA_MOVE_RIGHT);
	if(msg)
		renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
	msg = mApplication.inputInfoMsg("Press "," to move fast", CAMERA_SHIFT_SPEED, -1);
	if(msg)
		renderer->print(x, y += yInc, msg, scale, shadowOffset, textColor);
	msg = mApplication.inputInfoMsg("Press ","  to jump", CAMERA_JUMP,-1);
	if(msg)
		renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
	msg = mApplication.inputInfoMsg("Press ","  to crouch", CAMERA_CROUCH,-1);
	if(msg)
		renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);	;
	msg = mApplication.inputInfoMsg("Press "," to throw an object", SPAWN_DEBUG_OBJECT, -1);
	if(msg)
		renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);	;
	msg = mApplication.inputInfoMsg("Press ","  to reset CCT position", RETRY,-1);
	if(msg)
		renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
	msg = mApplication.inputInfoMsg("Press ","  to teleport from one platform to another", TELEPORT,-1);
	if(msg)
		renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
}

void SampleBridges::descriptionRender(PxU32 x, PxU32 y, PxU8 textAlpha)
{
	bool print=(textAlpha!=0.0f);

	if(print)
	{
		Renderer* renderer = getRenderer();
		const PxU32 yInc = 24;
		const PxReal scale = 0.5f;
		const PxReal shadowOffset = 6.0f;
		const RendererColor textColor(255, 255, 255, textAlpha);

		char line0[256]="This sample demonstrates the configuration and run-time control of the";
		char line1[256]="PhysX kinematic character controller (cct).  In particular, the";
		char line2[256]="interaction between the cct and both kinematic and dynamic scene objects";
		char line3[256]="is presented. Jointed bridges with a variety of configurations are also";
		char line4[256]="added to the scene to illustrate the setup code and behavior of some of";
		char line5[256]="the different joint types supported by the PhysX SDK.";

		renderer->print(x, y+=yInc, line0, scale, shadowOffset, textColor);
		renderer->print(x, y+=yInc, line1, scale, shadowOffset, textColor);
		renderer->print(x, y+=yInc, line2, scale, shadowOffset, textColor);
		renderer->print(x, y+=yInc, line3, scale, shadowOffset, textColor);
		renderer->print(x, y+=yInc, line4, scale, shadowOffset, textColor);
		renderer->print(x, y+=yInc, line5, scale, shadowOffset, textColor);
	}
}

///////////////////////////////////////////////////////////////////////////////

void SampleBridges::customizeRender()
{
	// PT: add a simple screenquad when underwater
	if(getCamera().getPos().y<mWaterY)
	{
		const RendererColor color(0, 90, 90);
		ScreenQuad sq;
		sq.mLeftUpColor		= color;
		sq.mRightUpColor	= color;
		sq.mLeftDownColor	= color;
		sq.mRightDownColor	= color;
		sq.mAlpha			= 0.75;

		getRenderer()->drawScreenQuad(sq);
	}
}

redpajama_set_name: RedPajamaGithub


text:
require 'spec_helper'

include UserAbilitiesHelper

describe "User Abilities Pages" do

  subject { page }

  let (:admin) { FactoryGirl.create(:admin) }
  let (:user) { FactoryGirl.create(:user) }

  before do
    sign_out
    sign_in admin
  end

  describe "access link to edit user privilegies in show user page" do
    before do
      visit user_path(user)
    end

    it { should have_link(I18n.t("users.abilities.link")) }

    describe "when click, go to user abilities pages" do
      before { click_link I18n.t("users.abilities.link") }
      it { should have_selector("h1", text: I18n.t("users.abilities.title", :user => user.name)) }
      it { should on_path(user_abilities_path(user)) }
    end

  end

  describe "edit multiple abilities and modules on index page " do
    before do
       able(user, :read,   "customer")
       able(user, :update, "contact")
       visit user_abilities_path(user)
    end

    it { should have_content(I18n.t("users.abilities.title", :user => user.name)) }

    it { should have_content(I18n.t("system.modules.%s" % SystemModule.USER.name.downcase)) }

    it { find(:id, abilities_id_checkbox(SystemModule.CUSTOMER, SystemAbility.READ)).should be_checked }

    it { find(:id, abilities_id_checkbox(SystemModule.CONTACT, SystemAbility.UPDATE)).should be_checked }

    it { should have_selector("input", value: I18n.t("helpers.forms.update")) }

    describe "add ability to User to Update Customer" do
      before do
        check abilities_id_checkbox(SystemModule.CUSTOMER, SystemAbility.CREATE)
      end

      it "add ability to user when check item" do
        expect { click_on I18n.t("helpers.forms.update") }.to change(UserAbility, :count).by(+1)
      end
    end

    describe "add ability to User to Update Customer" do
      before do
        check abilities_id_checkbox(SystemModule.CUSTOMER, SystemAbility.CREATE)
        check abilities_id_checkbox(SystemModule.CUSTOMER, SystemAbility.UPDATE)
        click_on I18n.t("helpers.forms.update")
      end

      it { user.able?(SystemModule.CUSTOMER, SystemAbility.CREATE).should be_true }
      it { user.able?(SystemModule.CUSTOMER, SystemAbility.UPDATE).should be_true }

    end
  end

end


text:
function Uninstall-CUser
{
    <#
    .SYNOPSIS
    Removes a user from the local computer.

    .DESCRIPTION
    Removes a *local* user account.  If the account doesn't exist, nothing happens.

    .LINK
    Get-CUser

    .LINK
    Install-CUser

    .LINK
    Test-CUser

    .LINK
    Uninstall-CUser

    .EXAMPLE
    Uninstall-CUser -Username WTarkin

    Removes the `WTarkin` *local* user account.
    #>
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true)]
        [ValidateLength(1,20)]
        [string]
        # The username of the account to remove.
        $Username
    )

    Set-StrictMode -Version 'Latest'

    Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState

    if( Test-CUser -Username $username )
    {
        $user = Get-CUser -Username $Username
        try
        {
            if( $pscmdlet.ShouldProcess( $Username, "remove local user" ) )
            {
                $user.Delete()
            }
        }
        finally
        {
            $user.Dispose()
        }
    }
}

Set-Alias -Name 'Remove-User' -Value 'Uninstall-CUser'


text:
package ro.stancalau.lombok.tests.base;

import org.junit.Before;
import ro.stancalau.lombok.api.ImmutableParent;
import ro.stancalau.lombok.api.MutablePerson;
import ro.stancalau.lombok.examples.correct.CorrectMutableParent;
import ro.stancalau.lombok.factory.ParentFactory;
import ro.stancalau.lombok.factory.ParentReflectionFactory;

import java.util.HashSet;
import java.util.Set;

import static ro.stancalau.lombok.tests.base.TestNames.BOY_NAME;
import static ro.stancalau.lombok.tests.base.TestNames.GIRL_NAME;

public abstract class ParentBaseTest<T extends ImmutableParent> extends ParameterizedTest implements ParentTest<T> {

    protected static final MutablePerson ILLEGITIMATE_CHILD = new CorrectMutableParent("Bob");
    protected Set<MutablePerson> globalChildren;

    private final ParentFactory<T> factory;

    @Before
    public void setUpChildren() throws Exception {
        globalChildren = new HashSet<>();
        globalChildren.add(new CorrectMutableParent(BOY_NAME));
        globalChildren.add(new CorrectMutableParent(GIRL_NAME));
    }

    public ParentBaseTest(Class<T> clazz) {
        factory = new ParentReflectionFactory<>(clazz);
    }

    @Override
    public T create(String name, Set<MutablePerson> children) {
        return factory.create(name, children);
    }

    @Override
    public T create(String name) {
        return factory.create(name);
    }

    @Override
    public T create() {
        return factory.create();
    }
}


text:
#ifndef API_TASK_QUEUE_TASK_QUEUE_BASE_H_
#define API_TASK_QUEUE_TASK_QUEUE_BASE_H_

#include <memory>

#include "api/task_queue/queued_task.h"
#include "rtc_base/system/rtc_export.h"
#include "rtc_base/thread_annotations.h"

namespace webrtc {

// Asynchronously executes tasks in a way that guarantees that they're executed
// in FIFO order and that tasks never overlap. Tasks may always execute on the
// same worker thread and they may not. To DCHECK that tasks are executing on a
// known task queue, use IsCurrent().
class RTC_LOCKABLE RTC_EXPORT TaskQueueBase {
 public:
  // Starts destruction of the task queue.
  // On return ensures no task are running and no new tasks are able to start
  // on the task queue.
  // Responsible for deallocation. Deallocation may happen syncrhoniously during
  // Delete or asynchronously after Delete returns.
  // Code not running on the TaskQueue should not make any assumption when
  // TaskQueue is deallocated and thus should not call any methods after Delete.
  // Code running on the TaskQueue should not call Delete, but can assume
  // TaskQueue still exists and may call other methods, e.g. PostTask.
  virtual void Delete() = 0;

  // Schedules a task to execute. Tasks are executed in FIFO order.
  // If |task->Run()| returns true, task is deleted on the task queue
  // before next QueuedTask starts executing.
  // When a TaskQueue is deleted, pending tasks will not be executed but they
  // will be deleted. The deletion of tasks may happen synchronously on the
  // TaskQueue or it may happen asynchronously after TaskQueue is deleted.
  // This may vary from one implementation to the next so assumptions about
  // lifetimes of pending tasks should not be made.
  virtual void PostTask(std::unique_ptr<QueuedTask> task) = 0;

  // Schedules a task to execute a specified number of milliseconds from when
  // the call is made. The precision should be considered as "best effort"
  // and in some cases, such as on Windows when all high precision timers have
  // been used up, can be off by as much as 15 millseconds.
  virtual void PostDelayedTask(std::unique_ptr<QueuedTask> task,
                               uint32_t milliseconds) = 0;

  // Returns the task queue that is running the current thread.
  // Returns nullptr if this thread is not associated with any task queue.
  static TaskQueueBase* Current();
  bool IsCurrent() const { return Current() == this; }

 protected:
  class CurrentTaskQueueSetter {
   public:
    explicit CurrentTaskQueueSetter(TaskQueueBase* task_queue);
    CurrentTaskQueueSetter(const CurrentTaskQueueSetter&) = delete;
    CurrentTaskQueueSetter& operator=(const CurrentTaskQueueSetter&) = delete;
    ~CurrentTaskQueueSetter();

   private:
    TaskQueueBase* const previous_;
  };

  // Users of the TaskQueue should call Delete instead of directly deleting
  // this object.
  virtual ~TaskQueueBase() = default;
};

struct TaskQueueDeleter {
  void operator()(TaskQueueBase* task_queue) const { task_queue->Delete(); }
};

}  // namespace webrtc

#endif  // API_TASK_QUEUE_TASK_QUEUE_BASE_H_


text:
module InyxCatalogRails
  class ApplicationController < ::ApplicationController
  end
end


text:
set(CMAKE_SYSTEM_NAME Linux)

include(CMakeForceCompiler)
CMAKE_FORCE_C_COMPILER(fccpx GNU)
CMAKE_FORCE_CXX_COMPILER(FCCpx GNU)
# CMAKE_FORCE_Fortran_Compiler is not supported ver. 2.6
#CMAKE_FORCE_Fortran_COMPILER(frtpx GNU)
set(CMAKE_Fortran_COMPILER frtpx)
set(CMAKE_Fortran_COMPILER_WORKS true)

#set(CMAKE_FIND_ROOT_PATH   /opt/FJSVfxlang/1.2.1)
set(CMAKE_FIND_ROOT_PATH   /opt/FJSVXosDevkit/sparc64fx/target)  #couldnt work 2015/1/28
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)


text:
export default (user) => ({
  id: user._id,
  email: user.email,
  login: user.login,
  friends: user.friends,
})


text:
<!DOCTYPE html>
<!--
Copyright 2015 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<script>
'use strict';

/**
 * Does a deep copy of the object.
 * @param {*} obj Object to copy.
 * @return {*} A copy of the object.
 */
function deepCopy(obj) {
  return JSON.parse(JSON.stringify(obj));
}

/**
 * JavaScript for graphing performance data.
 */
var graph = (function() {

  /**
   * Returns a hex string color from hash code of a string.  This a popular
   * implementation of generating hash code and spread over rgb color range
   * http://stackoverflow.com/questions/3426404.
   * @param {string} str string to get color code for.
   * @return {string} A rgb color code string.
   */
  var stringToColor = function(str) {
    var hash = hashCode_(str);
    var red = hash & 0xff;
    var green = (hash >> 8) & 0xff;
    var blue = (hash >> 16) & 0xff;
    return 'rgb(' + red + ',' + green + ',' + blue + ')';
  };

  /**
   * Gets a hash code for a string.
   * This is used by 'stringToColor' to generate a unique hash code
   * so that each string will always map to the same color.
   * @param {string} str A string to hash.
   * @return {number} An integer number.
   */
  var hashCode_ = function(str) {
    var hash = 0;
    for (var i = 0; i < str.length; i++) {
      hash = hash * 31 + str.charCodeAt(i);
      // Convert to 32-bit integer.
      hash = hash & hash;
    }
    return hash;
  };

  return {
    stringToColor: stringToColor
  };
})();

</script>


text:
<?xml version="1.0" encoding="UTF-8"?>
<ti:app xmlns:ti="http://ti.appcelerator.org">
    <deployment-targets>
        <target device="mobileweb">false</target>
        <target device="iphone">true</target>
        <target device="ipad">true</target>
        <target device="android">true</target>
        <target device="blackberry">false</target>
    </deployment-targets>
    <sdk-version>1.8.0.1</sdk-version>
    <id>com.matmannion.bidirectionalscroll</id>
    <name>bidirectional-scroll</name>
    <version>1.0</version>
    <publisher>mat</publisher>
    <url>http://www.matmannion.com</url>
    <description>not specified</description>
    <copyright>2012 by Mat Mannion</copyright>
    <icon>appicon.png</icon>
    <persistent-wifi>false</persistent-wifi>
    <prerendered-icon>false</prerendered-icon>
    <statusbar-style>default</statusbar-style>
    <statusbar-hidden>false</statusbar-hidden>
    <fullscreen>false</fullscreen>
    <navbar-hidden>false</navbar-hidden>
    <analytics>true</analytics>
    <guid>e1b89f85-fe91-447a-9d4c-f280962aba2b</guid>
    <iphone>
        <orientations device="iphone">
            <orientation>Ti.UI.PORTRAIT</orientation>
        </orientations>
        <orientations device="ipad">
            <orientation>Ti.UI.PORTRAIT</orientation>
            <orientation>Ti.UI.UPSIDE_PORTRAIT</orientation>
            <orientation>Ti.UI.LANDSCAPE_LEFT</orientation>
            <orientation>Ti.UI.LANDSCAPE_RIGHT</orientation>
        </orientations>
    </iphone>
    <android xmlns:android="http://schemas.android.com/apk/res/android"/>
    <modules/>
</ti:app>


text:
package org.thingsboard.server.common.data.security.model.mfa.provider;

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = true)
public class EmailTwoFaProviderConfig extends OtpBasedTwoFaProviderConfig {

    @Override
    public TwoFaProviderType getProviderType() {
        return TwoFaProviderType.EMAIL;
    }

}


text:
module MkmApi
  module Resources
    class Name < Hashie::Dash
      include Hashie::Extensions::Dash::Coercion, Hashie::Extensions::Dash::PropertyTranslation

      property :first_name, from: :firstName
      property :last_name, from: :lastName
    end
  end
end


text:
package com.vityuk.ginger.provider.plural.impl;

/**
 * Plural rules for Malayalam language.
 *
 * @author Andriy Vityuk
 */
public final class PluralRule_ml extends PluralRule_1_0n {
}


text:
import mongoose from 'mongoose';
import Message from './Message';

import Speaker from './Speaker';

const Schema = mongoose.Schema,
      Types  = mongoose.Schema.Types;

const Room = new Schema({
	name        : Types.String,
	occupants   : [Speaker],
	conversation: [Message]
});

export default mongoose.model('Room', Room);