package aerys.minko.render.shader
{
	import aerys.minko.ns.minko_render;
	import aerys.minko.ns.minko_shader;
	import aerys.minko.render.RenderTarget;
	import aerys.minko.render.resource.Context3DResource;
	import aerys.minko.render.resource.Program3DResource;
	import aerys.minko.render.shader.compiler.graph.ShaderGraph;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;
	import aerys.minko.render.shader.part.ShaderPart;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;

	import flash.utils.getQualifiedClassName;
	import aerys.minko.type.binding.Signature;
	import aerys.minko.render.ShaderDataBindingsProxy;

	use namespace minko_shader;

	/**
	 * The base class to extend in order to create ActionScript shaders
	 * and program the GPU using AS3.
	 *
	 * @author Jean-Marc Le Roux
	 * @author Romain Gilliotte
	 *
	 * @see aerys.minko.render.shader.ShaderPart
	 * @see aerys.minko.render.shader.ShaderInstance
	 * @see aerys.minko.render.shader.ShaderSignature
	 * @see aerys.minko.render.shader.ShaderDataBindings
	 */
	public class Shader extends ShaderPart
	{
		use namespace minko_shader;
		use namespace minko_render;

		minko_shader var _meshBindings		: ShaderDataBindingsProxy		= null;
		minko_shader var _sceneBindings		: ShaderDataBindingsProxy		= null;
		minko_shader var _kills				: Vector.<AbstractNode>			= new <AbstractNode>[];

		private var _name					: String						= null;
		private var _enabled				: Boolean						= true;
		private var _defaultSettings		: ShaderSettings				= new ShaderSettings(null);

		private var _instances				: Vector.<ShaderInstance>		= new <ShaderInstance>[];
		private var _numActiveInstances		: uint							= 0;
		private var _numRenderedInstances	: uint							= 0;
		private var _settings				: Vector.<ShaderSettings>		= new <ShaderSettings>[];
		private var _programs				: Vector.<Program3DResource>	= new <Program3DResource>[];

		private var _begin					: Signal						= new Signal('Shader.begin');
		private var _end					: Signal						= new Signal('Shader.end');

		/**
		 * The name of the shader. Default value is the qualified name of the
		 * ActionScript shader class (example: "aerys.minko.render.effect.basic::BasicShader").
		 *
		 * @return
		 *
		 */
		public function get name() : String
		{
			return _name;
		}
		public function set name(value : String) : void
		{
			_name = value;
		}

		/**
		 * The signal executed when the shader (one of its forks) is used
		 * for rendering for the very first time during the current frame.
		 * Callbacks for this signal must accept the following arguments:
		 *

		 *
shader : Shader, the shader executing the signal

		 *
context : Context3D, the context used for rendering

		 *
backBuffer : RenderTarget, the render target used as the back-buffer

		 *

		 *
		 * @return
		 *
		 */
		public function get begin() : Signal
		{
			return _begin;
		}

		/**
		 * The signal executed when the shader (one of its forks) is used
		 * for rendering for the very last time during the current frame.
		 * Callbacks for this signal must accept the following arguments:
		 *

		 *
shader : Shader, the shader executing the signal

		 *
context : Context3D, the context used for rendering

		 *
backBuffer : RenderTarget, the render target used as the back-buffer

		 *

		 *
		 * @return
		 *
		 */
		public function get end() : Signal
		{
			return _end;
		}

		/**
		 * Whether the shader (and all its forks) are enabled for rendering
		 * or not.
		 *
		 * @return
		 *
		 */
		public function get enabled() : Boolean
		{
			return _enabled;
		}
		public function set enabled(value : Boolean) : void
		{
			_enabled = value;
		}

		/**
		 *
		 * @param priority Default value is 0.
		 * @param renderTarget Default value is null.
		 *
		 */
		public function Shader(renderTarget	: RenderTarget	= null,
							   priority		: Number		= 0.0)
		{
			super(this);

			_defaultSettings.renderTarget = renderTarget;
			_defaultSettings.priority = priority;

			_name = getQualifiedClassName(this);
		}

		public function fork(sceneBindings	: DataBindings,
							 meshBindings	: DataBindings) : ShaderInstance
		{
			var pass : ShaderInstance = findPass(sceneBindings, meshBindings);

			if (!pass)
			{
				var signature	: Signature			= new Signature();
				var config		: ShaderSettings	= findOrCreateSettings(
					sceneBindings, meshBindings
				);

				signature.mergeWith(config.signature);

				var program		: Program3DResource	= null;

				if (config.enabled)
				{
					program = findOrCreateProgram(sceneBindings, meshBindings);
					signature.mergeWith(program.signature);
				}

				pass = new ShaderInstance(this, config, program, signature);
				pass.retained.add(shaderInstanceRetainedHandler);
				pass.released.add(shaderInstanceReleasedHandler);

				_instances.push(pass);
			}

			return pass;
		}

		public function disposeUnusedResources() : void
		{
			var numInstances	: uint = _instances.length;
			var currentId		: uint = 0;

			for (var instanceId : uint = 0; instanceId < numInstances; ++instanceId)
			{
				var passInstance : ShaderInstance = _instances[instanceId];

				if (!passInstance.isDisposable)
					_instances[currentId++] = passInstance;
			}

			_instances.length = currentId;
		}

		/**
		 * Override this method to initialize the settings
		 * - such as the blending operands or the triangle culling - of the shader.
		 * This values can be read from both the mesh and the scene bindings.
		 *
		 * @param settings
		 *
		 * @see aerys.minko.render.shader.ShaderSettings
		 *
		 */
		protected function initializeSettings(settings : ShaderSettings) : void
		{
			// nothing
		}

		/**
		 * The getVertexPosition() method is called to evaluate the vertex shader
		 * program that shall be executed on the GPU.
		 *
		 * @return The position of the vertex in clip space (normalized screen space).
		 *
		 */
		protected function getVertexPosition() : SFloat
		{
			throw new Error("The method 'getVertexPosition' must be implemented.");
		}

		/**
		 * The getPixelColor() method is called to evaluate the fragment shader
		 * program that shall be executed on the GPU.
		 *
		 * @return The color of the pixel on the screen.
		 *
		 */
		protected function getPixelColor() : SFloat
		{
			throw new Error("The method 'getPixelColor' must be implemented.");
		}

		private function findPass(sceneBindings	: DataBindings,
								  meshBindings	: DataBindings) : ShaderInstance
		{
			var numPasses : int = _instances.length;

			for (var passId : uint = 0; passId < numPasses; ++passId)
				if (_instances[passId].signature.isValid(sceneBindings, meshBindings))
					return _instances[passId];

			return null;
		}

		private function findOrCreateProgram(sceneBindings	: DataBindings,
											 meshBindings	: DataBindings) : Program3DResource
		{
			var numPrograms	: int = _programs.length;
			var program 	: Program3DResource;

			for (var programId : uint = 0; programId < numPrograms; ++programId)
				if (_programs[programId].signature.isValid(sceneBindings, meshBindings))
					return _programs[programId];

			var signature		: Signature		= new Signature();

			_meshBindings	= new ShaderDataBindingsProxy(meshBindings, signature, Signature.SOURCE_MESH);
			_sceneBindings	= new ShaderDataBindingsProxy(sceneBindings, signature, Signature.SOURCE_SCENE);

			var vertexPosition	: AbstractNode	= getVertexPosition()._node;
			var pixelColor		: AbstractNode	= getPixelColor()._node;
			var shaderGraph		: ShaderGraph	= new ShaderGraph(vertexPosition, pixelColor, _kills);

			program			= shaderGraph.generateProgram(_name, signature);
			_meshBindings	= null;
			_sceneBindings	= null;
			_kills.length	= 0;

			_programs.push(program);

			return program;
		}

		private function findOrCreateSettings(sceneBindings	: DataBindings,
											  meshBindings 	: DataBindings) : ShaderSettings
		{
			var numConfigs	: int 				= _settings.length;
			var config		: ShaderSettings	= null;

			for (var configId : int = 0; configId < numConfigs; ++configId)
				if (_settings[configId].signature.isValid(sceneBindings, meshBindings))
					return _settings[configId];

			var signature : Signature = new Signature();

			config			= _defaultSettings.clone(signature);
			_meshBindings	= new ShaderDataBindingsProxy(meshBindings, signature, Signature.SOURCE_MESH);
			_sceneBindings	= new ShaderDataBindingsProxy(sceneBindings, signature, Signature.SOURCE_SCENE);

			initializeSettings(config);

			_meshBindings	= null;
			_sceneBindings	= null;

			_settings.push(config);

			return config;
		}

		private function shaderInstanceRetainedHandler(instance : ShaderInstance,
													   numUses	: uint) : void
		{
			if (numUses == 1)
			{
				++_numActiveInstances;

				instance.begin.add(shaderInstanceBeginHandler);
				instance.end.add(shaderInstanceEndHandler);
			}
		}

		private function shaderInstanceReleasedHandler(instance : ShaderInstance,
													   numUses	: uint) : void
		{
			if (numUses == 0)
			{
				--_numActiveInstances;

				instance.begin.remove(shaderInstanceBeginHandler);
				instance.end.remove(shaderInstanceEndHandler);
			}
		}

		private function shaderInstanceBeginHandler(instance 	: ShaderInstance,
													context		: Context3DResource,
													backBuffer	: RenderTarget) : void
		{
			if (_numRenderedInstances == 0)
				_begin.execute(this, context, backBuffer);
		}

		private function shaderInstanceEndHandler(instance		: ShaderInstance,
												  context		: Context3DResource,
												  backBuffer	: RenderTarget) : void
		{
			_numRenderedInstances++;

			if (_numRenderedInstances == _numActiveInstances)
			{
				_numRenderedInstances = 0;
				_end.execute(this, context, backBuffer);
			}
		}
	}
}

package aerys.minko.render.shader
{
	import aerys.minko.ns.minko_render;
	import aerys.minko.ns.minko_shader;
	import aerys.minko.render.RenderTarget;
	import aerys.minko.render.ShaderDataBindingsProxy;
	import aerys.minko.render.resource.Context3DResource;
	import aerys.minko.render.resource.Program3DResource;
	import aerys.minko.render.shader.compiler.graph.ShaderGraph;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;
	import aerys.minko.render.shader.part.ShaderPart;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.Signature;

	import flash.utils.getQualifiedClassName;

	use namespace minko_shader;

	/**
	 * The base class to extend in order to create ActionScript shaders
	 * and program the GPU using AS3.
	 *
	 * @author Jean-Marc Le Roux
	 * @author Romain Gilliotte
	 *
	 * @see aerys.minko.render.shader.ShaderPart
	 * @see aerys.minko.render.shader.ShaderInstance
	 * @see aerys.minko.render.shader.ShaderSignature
	 * @see aerys.minko.render.shader.ShaderDataBindings
	 */
	public class Shader extends ShaderPart
	{
		use namespace minko_shader;
		use namespace minko_render;

		minko_shader var _meshBindings		: ShaderDataBindingsProxy		= null;
		minko_shader var _sceneBindings		: ShaderDataBindingsProxy		= null;
		minko_shader var _kills				: Vector.<AbstractNode>			= new <AbstractNode>[];

		private var _name					: String						= null;
		private var _enabled				: Boolean						= true;
		private var _defaultSettings		: ShaderSettings				= new ShaderSettings(null);

		private var _instances				: Vector.<ShaderInstance>		= new <ShaderInstance>[];
		private var _numActiveInstances		: uint							= 0;
		private var _numRenderedInstances	: uint							= 0;
		private var _settings				: Vector.<ShaderSettings>		= new <ShaderSettings>[];
		private var _programs				: Vector.<Program3DResource>	= new <Program3DResource>[];

		private var _begin					: Signal						= new Signal('Shader.begin');
		private var _end					: Signal						= new Signal('Shader.end');

		/**
		 * The name of the shader. Default value is the qualified name of the
		 * ActionScript shader class (example: "aerys.minko.render.effect.basic::BasicShader").
		 *
		 * @return
		 *
		 */
		public function get name() : String
		{
			return _name;
		}
		public function set name(value : String) : void
		{
			_name = value;
		}

		/**
		 * The signal executed when the shader (one of its forks) is used
		 * for rendering for the very first time during the current frame.
		 * Callbacks for this signal must accept the following arguments:
		 *

		 *
shader : Shader, the shader executing the signal

		 *
context : Context3D, the context used for rendering

		 *
backBuffer : RenderTarget, the render target used as the back-buffer

		 *

		 *
		 * @return
		 *
		 */
		public function get begin() : Signal
		{
			return _begin;
		}

		/**
		 * The signal executed when the shader (one of its forks) is used
		 * for rendering for the very last time during the current frame.
		 * Callbacks for this signal must accept the following arguments:
		 *

		 *
shader : Shader, the shader executing the signal

		 *
context : Context3D, the context used for rendering

		 *
backBuffer : RenderTarget, the render target used as the back-buffer

		 *

		 *
		 * @return
		 *
		 */
		public function get end() : Signal
		{
			return _end;
		}

		/**
		 * Whether the shader (and all its forks) are enabled for rendering
		 * or not.
		 *
		 * @return
		 *
		 */
		public function get enabled() : Boolean
		{
			return _enabled;
		}
		public function set enabled(value : Boolean) : void
		{
			_enabled = value;
		}

		/**
		 *
		 * @param priority Default value is 0.
		 * @param renderTarget Default value is null.
		 *
		 */
		public function Shader(renderTarget	: RenderTarget	= null,
							   priority		: Number		= 0.0)
		{
			super(this);

			_defaultSettings.renderTarget = renderTarget;
			_defaultSettings.priority = priority;

			_name = getQualifiedClassName(this);
		}

		public function fork(sceneBindings	: DataBindings,
							 meshBindings	: DataBindings) : ShaderInstance
		{
			var pass : ShaderInstance = findPass(sceneBindings, meshBindings);

			if (!pass)
			{
				var signature	: Signature			= new Signature();
				var config		: ShaderSettings	= findOrCreateSettings(
					sceneBindings, meshBindings
				);

				signature.mergeWith(config.signature);

				var program		: Program3DResource	= null;

				if (config.enabled)
				{
					program = findOrCreateProgram(sceneBindings, meshBindings);
					signature.mergeWith(program.signature);
				}

				pass = new ShaderInstance(this, config, program, signature);
				pass.retained.add(shaderInstanceRetainedHandler);
				pass.released.add(shaderInstanceReleasedHandler);

				_instances.push(pass);
			}

			return pass;
		}

		public function disposeUnusedResources() : void
		{
			var numInstances	: uint = _instances.length;
			var currentId		: uint = 0;

			for (var instanceId : uint = 0; instanceId < numInstances; ++instanceId)
			{
				var passInstance : ShaderInstance = _instances[instanceId];

				if (!passInstance.isDisposable)
					_instances[currentId++] = passInstance;
			}

			_instances.length = currentId;
		}

		/**
		 * Override this method to initialize the settings
		 * - such as the blending operands or the triangle culling - of the shader.
		 * This values can be read from both the mesh and the scene bindings.
		 *
		 * @param settings
		 *
		 * @see aerys.minko.render.shader.ShaderSettings
		 *
		 */
		protected function initializeSettings(settings : ShaderSettings) : void
		{
			// nothing
		}

		/**
		 * The getVertexPosition() method is called to evaluate the vertex shader
		 * program that shall be executed on the GPU.
		 *
		 * @return The position of the vertex in clip space (normalized screen space).
		 *
		 */
		protected function getVertexPosition() : SFloat
		{
			throw new Error("The method 'getVertexPosition' must be implemented.");
		}

		/**
		 * The getPixelColor() method is called to evaluate the fragment shader
		 * program that shall be executed on the GPU.
		 *
		 * @return The color of the pixel on the screen.
		 *
		 */
		protected function getPixelColor() : SFloat
		{
			throw new Error("The method 'getPixelColor' must be implemented.");
		}

		private function findPass(sceneBindings	: DataBindings,
								  meshBindings	: DataBindings) : ShaderInstance
		{
			var numPasses : int = _instances.length;

			for (var passId : uint = 0; passId < numPasses; ++passId)
				if (_instances[passId].signature.isValid(sceneBindings, meshBindings))
					return _instances[passId];

			return null;
		}

		private function findOrCreateProgram(sceneBindings	: DataBindings,
											 meshBindings	: DataBindings) : Program3DResource
		{
			var numPrograms	: int = _programs.length;
			var program 	: Program3DResource;

			for (var programId : uint = 0; programId < numPrograms; ++programId)
				if (_programs[programId].signature.isValid(sceneBindings, meshBindings))
					return _programs[programId];

			var signature		: Signature		= new Signature();

			_meshBindings	= new ShaderDataBindingsProxy(meshBindings, signature, Signature.SOURCE_MESH);
			_sceneBindings	= new ShaderDataBindingsProxy(sceneBindings, signature, Signature.SOURCE_SCENE);

			var vertexPosition	: AbstractNode	= getVertexPosition()._node;
			var pixelColor		: AbstractNode	= getPixelColor()._node;
			var shaderGraph		: ShaderGraph	= new ShaderGraph(vertexPosition, pixelColor, _kills);

			program			= shaderGraph.generateProgram(_name, signature);
			_meshBindings	= null;
			_sceneBindings	= null;
			_kills.length	= 0;

			_programs.push(program);

			return program;
		}

		private function findOrCreateSettings(sceneBindings	: DataBindings,
											  meshBindings 	: DataBindings) : ShaderSettings
		{
			var numConfigs	: int 				= _settings.length;
			var config		: ShaderSettings	= null;

			for (var configId : int = 0; configId < numConfigs; ++configId)
				if (_settings[configId].signature.isValid(sceneBindings, meshBindings))
					return _settings[configId];

			var signature : Signature = new Signature();

			config			= _defaultSettings.clone(signature);
			_meshBindings	= new ShaderDataBindingsProxy(meshBindings, signature, Signature.SOURCE_MESH);
			_sceneBindings	= new ShaderDataBindingsProxy(sceneBindings, signature, Signature.SOURCE_SCENE);

			initializeSettings(config);

			_meshBindings	= null;
			_sceneBindings	= null;

			_settings.push(config);

			return config;
		}

		private function shaderInstanceRetainedHandler(instance : ShaderInstance,
													   numUses	: uint) : void
		{
			if (numUses == 1)
			{
				++_numActiveInstances;

				instance.begin.add(shaderInstanceBeginHandler);
				instance.end.add(shaderInstanceEndHandler);
			}
		}

		private function shaderInstanceReleasedHandler(instance : ShaderInstance,
													   numUses	: uint) : void
		{
			if (numUses == 0)
			{
				--_numActiveInstances;

				instance.begin.remove(shaderInstanceBeginHandler);
				instance.end.remove(shaderInstanceEndHandler);
			}
		}

		private function shaderInstanceBeginHandler(instance 	: ShaderInstance,
													context		: Context3DResource,
													backBuffer	: RenderTarget) : void
		{
			if (_numRenderedInstances == 0)
				_begin.execute(this, context, backBuffer);
		}

		private function shaderInstanceEndHandler(instance		: ShaderInstance,
												  context		: Context3DResource,
												  backBuffer	: RenderTarget) : void
		{
			_numRenderedInstances++;

			if (_numRenderedInstances == _numActiveInstances)
			{
				_numRenderedInstances = 0;
				_end.execute(this, context, backBuffer);
			}
		}
	}
}

refactor imports

refactor imports


package aerys.minko.render.shader
{
	import aerys.minko.ns.minko_render;
	import aerys.minko.ns.minko_shader;
	import aerys.minko.render.RenderTarget;
	import aerys.minko.render.resource.Context3DResource;
	import aerys.minko.render.resource.Program3DResource;
	import aerys.minko.render.shader.compiler.graph.ShaderGraph;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;
	import aerys.minko.render.shader.part.ShaderPart;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;

	import flash.utils.getQualifiedClassName;

	use namespace minko_shader;

	/**
	 * The base class to extend in order to create ActionScript shaders
	 * and program the GPU using AS3.
	 *
	 * @author Jean-Marc Le Roux
	 * @author Romain Gilliotte
	 *
	 * @see aerys.minko.render.shader.ShaderPart
	 * @see aerys.minko.render.shader.ShaderInstance
	 * @see aerys.minko.render.shader.ShaderSignature
	 * @see aerys.minko.render.shader.ShaderDataBindings
	 */
	public class Shader extends ShaderPart
	{
		use namespace minko_shader;
		use namespace minko_render;

		minko_shader var _meshBindings		: ShaderDataBindings			= null;
		minko_shader var _sceneBindings		: ShaderDataBindings			= null;
		minko_shader var _kills				: Vector.<AbstractNode>			= new <AbstractNode>[];

		private var _name					: String						= null;
		private var _enabled				: Boolean						= true;
		private var _defaultSettings		: ShaderSettings				= new ShaderSettings(null);

		private var _instances				: Vector.<ShaderInstance>		= new <ShaderInstance>[]
		private var _numActiveInstances		: uint							= 0;
		private var _numRenderedInstances	: uint							= 0;
		private var _settings				: Vector.<ShaderSettings>		= new <ShaderSettings>[];
		private var _programs				: Vector.<Program3DResource>	= new <Program3DResource>[];

		private var _begin					: Signal						= new Signal('Shader.begin');
		private var _end					: Signal						= new Signal('Shader.end');

		/**
		 * The name of the shader. Default value is the qualified name of the
		 * ActionScript shader class (example: "aerys.minko.render.effect.basic::BasicShader").
		 *
		 * @return
		 *
		 */
		public function get name() : String
		{
			return _name;
		}
		public function set name(value : String) : void
		{
			_name = value;
		}

		/**
		 * The signal executed when the shader (one of its forks) is used
		 * for rendering for the very first time during the current frame.
		 * Callbacks for this signal must accept the following arguments:
		 *

		 *
shader : Shader, the shader executing the signal

		 *
context : Context3D, the context used for rendering

		 *
backBuffer : RenderTarget, the render target used as the back-buffer

		 *

		 *
		 * @return
		 *
		 */
		public function get begin() : Signal
		{
			return _begin;
		}

		/**
		 * The signal executed when the shader (one of its forks) is used
		 * for rendering for the very last time during the current frame.
		 * Callbacks for this signal must accept the following arguments:
		 *

		 *
shader : Shader, the shader executing the signal

		 *
context : Context3D, the context used for rendering

		 *
backBuffer : RenderTarget, the render target used as the back-buffer

		 *

		 *
		 * @return
		 *
		 */
		public function get end() : Signal
		{
			return _end;
		}

		/**
		 * Whether the shader (and all its forks) are enabled for rendering
		 * or not.
		 *
		 * @return
		 *
		 */
		public function get enabled() : Boolean
		{
			return _enabled;
		}
		public function set enabled(value : Boolean) : void
		{
			_enabled = value;
		}

		/**
		 *
		 * @param priority Default value is 0.
		 * @param renderTarget Default value is null.
		 *
		 */
		public function Shader(renderTarget	: RenderTarget	= null,
							   priority		: Number		= 0.0)
		{
			super(this);

			_defaultSettings.renderTarget = renderTarget;
			_defaultSettings.priority = priority;

			_name = getQualifiedClassName(this);
		}

		public function fork(meshBindings	: DataBindings,
							 sceneBindings	: DataBindings) : ShaderInstance
		{
			var pass : ShaderInstance = findPass(meshBindings, sceneBindings);

			if (pass == null)
			{
				var signature	: Signature			= new Signature(_name);
				var config		: ShaderSettings	= findOrCreateSettings(meshBindings, sceneBindings);

				signature.mergeWith(config.signature);

				var program		: Program3DResource	= null;

				if (config.enabled)
				{
					program = findOrCreateProgram(meshBindings, sceneBindings);
					signature.mergeWith(program.signature);
				}

				pass = new ShaderInstance(this, config, program, signature);
				pass.retained.add(shaderInstanceRetainedHandler);
				pass.released.add(shaderInstanceReleasedHandler);

				_instances.push(pass);
			}

			return pass;
		}

		public function disposeUnusedResources() : void
		{
			var numInstances	: uint = _instances.length;
			var currentId		: uint = 0;

			for (var instanceId : uint = 0; instanceId < numInstances; ++instanceId)
			{
				var passInstance : ShaderInstance = _instances[instanceId];

				if (!passInstance.isDisposable)
					_instances[currentId++] = passInstance;
			}

			_instances.length = currentId;
		}

		/**
		 * Override this method to initialize the settings
		 * - such as the blending operands or the triangle culling - of the shader.
		 * This values can be read from both the mesh and the scene bindings.
		 *
		 * @param settings
		 *
		 * @see aerys.minko.render.shader.ShaderSettings
		 *
		 */
		protected function initializeSettings(settings : ShaderSettings) : void
		{
//			throw new Error("The method 'initializeSettings' must be implemented.");
		}

		/**
		 * The getVertexPosition() method is called to evaluate the vertex shader
		 * program that shall be executed on the GPU.
		 *
		 * @return The position of the vertex in clip space (normalized screen space).
		 *
		 */
		protected function getVertexPosition() : SFloat
		{
			throw new Error("The method 'getVertexPosition' must be implemented.");
		}

		/**
		 * The getPixelColor() method is called to evaluate the fragment shader
		 * program that shall be executed on the GPU.
		 *
		 * @return The color of the pixel on the screen.
		 *
		 */
		protected function getPixelColor() : SFloat
		{
			throw new Error("The method 'getPixelColor' must be implemented.");
		}

		private function findPass(meshBindings	: DataBindings,
								  sceneBindings	: DataBindings) : ShaderInstance
		{
			var numPasses : int = _instances.length;

			for (var passId : uint = 0; passId < numPasses; ++passId)
				if (_instances[passId].signature.isValid(meshBindings, sceneBindings))
					return _instances[passId];

			return null;
		}

		private function findOrCreateProgram(meshBindings	: DataBindings,
											 sceneBindings	: DataBindings) : Program3DResource
		{
			var numPrograms	: int = _programs.length;
			var program 	: Program3DResource;

			for (var programId : uint = 0; programId < numPrograms; ++programId)
				if (_programs[programId].signature.isValid(meshBindings, sceneBindings))
					return _programs[programId];

			var signature		: Signature		= new Signature(_name);

			_meshBindings	= new ShaderDataBindings(meshBindings, signature, Signature.SOURCE_MESH);
			_sceneBindings	= new ShaderDataBindings(sceneBindings, signature, Signature.SOURCE_SCENE);

			var vertexPosition	: AbstractNode	= getVertexPosition()._node;
			var pixelColor		: AbstractNode	= getPixelColor()._node;
			var shaderGraph		: ShaderGraph	= new ShaderGraph(vertexPosition, pixelColor, _kills);

			program			= shaderGraph.generateProgram(_name, signature);
			_meshBindings	= null;
			_sceneBindings	= null;
			_kills.length	= 0;

			_programs.push(program);

			return program;
		}

		private function findOrCreateSettings(meshBindings 	: DataBindings,
											 sceneBindings	: DataBindings) : ShaderSettings
		{
			var numConfigs	: int 				= _settings.length;
			var config		: ShaderSettings	= null;

			for (var configId : int = 0; configId < numConfigs; ++configId)
				if (_settings[configId].signature.isValid(meshBindings, sceneBindings))
					return _settings[configId];

			var signature : Signature = new Signature(_name);

			config			= _defaultSettings.clone(signature);
			_meshBindings	= new ShaderDataBindings(meshBindings, signature, Signature.SOURCE_MESH);
			_sceneBindings	= new ShaderDataBindings(sceneBindings, signature, Signature.SOURCE_SCENE);

			initializeSettings(config);

			_meshBindings	= null;
			_sceneBindings	= null;

			_settings.push(config);

			return config;
		}

		private function shaderInstanceRetainedHandler(instance : ShaderInstance,
													   numUses	: uint) : void
		{
			if (numUses == 1)
			{
				++_numActiveInstances;

				instance.begin.add(shaderInstanceBeginHandler);
				instance.end.add(shaderInstanceEndHandler);
			}
		}

		private function shaderInstanceReleasedHandler(instance : ShaderInstance,
													   numUses	: uint) : void
		{
			if (numUses == 0)
			{
				--_numActiveInstances;

				instance.begin.remove(shaderInstanceBeginHandler);
				instance.end.remove(shaderInstanceEndHandler);
			}
		}

		private function shaderInstanceBeginHandler(instance 	: ShaderInstance,
													context		: Context3DResource,
													backBuffer	: RenderTarget) : void
		{
			if (_numRenderedInstances == 0)
				_begin.execute(this, context, backBuffer);
		}

		private function shaderInstanceEndHandler(instance		: ShaderInstance,
												  context		: Context3DResource,
												  backBuffer	: RenderTarget) : void
		{
			_numRenderedInstances++;

			if (_numRenderedInstances == _numActiveInstances)
			{
				_numRenderedInstances = 0;
				_end.execute(this, context, backBuffer);
			}
		}
	}
}

package aerys.minko.render.shader
{
	import aerys.minko.ns.minko_render;
	import aerys.minko.ns.minko_shader;
	import aerys.minko.render.RenderTarget;
	import aerys.minko.render.resource.Context3DResource;
	import aerys.minko.render.resource.Program3DResource;
	import aerys.minko.render.shader.compiler.graph.ShaderGraph;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;
	import aerys.minko.render.shader.part.ShaderPart;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;

	import flash.utils.getQualifiedClassName;

	use namespace minko_shader;

	/**
	 * The base class to extend in order to create ActionScript shaders
	 * and program the GPU using AS3.
	 *
	 * @author Jean-Marc Le Roux
	 * @author Romain Gilliotte
	 *
	 * @see aerys.minko.render.shader.ShaderPart
	 * @see aerys.minko.render.shader.ShaderInstance
	 * @see aerys.minko.render.shader.ShaderSignature
	 * @see aerys.minko.render.shader.ShaderDataBindings
	 */
	public class Shader extends ShaderPart
	{
		use namespace minko_shader;
		use namespace minko_render;

		minko_shader var _meshBindings		: ShaderDataBindings			= null;
		minko_shader var _sceneBindings		: ShaderDataBindings			= null;
		minko_shader var _kills				: Vector.<AbstractNode>			= new <AbstractNode>[];

		private var _name					: String						= null;
		private var _enabled				: Boolean						= true;
		private var _defaultSettings		: ShaderSettings				= new ShaderSettings(null);

		private var _instances				: Vector.<ShaderInstance>		= new <ShaderInstance>[]
		private var _numActiveInstances		: uint							= 0;
		private var _numRenderedInstances	: uint							= 0;
		private var _settings				: Vector.<ShaderSettings>		= new <ShaderSettings>[];
		private var _programs				: Vector.<Program3DResource>	= new <Program3DResource>[];

		private var _begin					: Signal						= new Signal('Shader.begin');
		private var _end					: Signal						= new Signal('Shader.end');

		/**
		 * The name of the shader. Default value is the qualified name of the
		 * ActionScript shader class (example: "aerys.minko.render.effect.basic::BasicShader").
		 *
		 * @return
		 *
		 */
		public function get name() : String
		{
			return _name;
		}
		public function set name(value : String) : void
		{
			_name = value;
		}

		/**
		 * The signal executed when the shader (one of its forks) is used
		 * for rendering for the very first time during the current frame.
		 * Callbacks for this signal must accept the following arguments:
		 *

		 *
shader : Shader, the shader executing the signal

		 *
context : Context3D, the context used for rendering

		 *
backBuffer : RenderTarget, the render target used as the back-buffer

		 *

		 *
		 * @return
		 *
		 */
		public function get begin() : Signal
		{
			return _begin;
		}

		/**
		 * The signal executed when the shader (one of its forks) is used
		 * for rendering for the very last time during the current frame.
		 * Callbacks for this signal must accept the following arguments:
		 *

		 *
shader : Shader, the shader executing the signal

		 *
context : Context3D, the context used for rendering

		 *
backBuffer : RenderTarget, the render target used as the back-buffer

		 *

		 *
		 * @return
		 *
		 */
		public function get end() : Signal
		{
			return _end;
		}

		/**
		 * Whether the shader (and all its forks) are enabled for rendering
		 * or not.
		 *
		 * @return
		 *
		 */
		public function get enabled() : Boolean
		{
			return _enabled;
		}
		public function set enabled(value : Boolean) : void
		{
			_enabled = value;
		}

		/**
		 *
		 * @param priority Default value is 0.
		 * @param renderTarget Default value is null.
		 *
		 */
		public function Shader(renderTarget	: RenderTarget	= null,
							   priority		: Number		= 0.0)
		{
			super(this);

			_defaultSettings.renderTarget = renderTarget;
			_defaultSettings.priority = priority;

			_name = getQualifiedClassName(this);
		}

		public function fork(meshBindings	: DataBindings,
							 sceneBindings	: DataBindings) : ShaderInstance
		{
			var pass : ShaderInstance = findPass(meshBindings, sceneBindings);

			if (pass == null)
			{
				var signature	: Signature			= new Signature(_name);
				var config		: ShaderSettings	= findOrCreateSettings(meshBindings, sceneBindings);

				signature.mergeWith(config.signature);

				var program		: Program3DResource	= null;

				if (config.enabled)
				{
					program = findOrCreateProgram(meshBindings, sceneBindings);
					signature.mergeWith(program.signature);
				}

				pass = new ShaderInstance(this, config, program, signature);
				pass.retained.add(shaderInstanceRetainedHandler);
				pass.released.add(shaderInstanceReleasedHandler);

				_instances.push(pass);
			}

			return pass;
		}

		public function disposeUnusedResources() : void
		{
			var numInstances	: uint = _instances.length;
			var currentId		: uint = 0;

			for (var instanceId : uint = 0; instanceId < numInstances; ++instanceId)
			{
				var passInstance : ShaderInstance = _instances[instanceId];

				if (!passInstance.isDisposable)
					_instances[currentId++] = passInstance;
			}

			_instances.length = currentId;
		}

		/**
		 * Override this method to initialize the settings
		 * - such as the blending operands or the triangle culling - of the shader.
		 * This values can be read from both the mesh and the scene bindings.
		 *
		 * @param settings
		 *
		 * @see aerys.minko.render.shader.ShaderSettings
		 *
		 */
		protected function initializeSettings(settings : ShaderSettings) : void
		{
			// nothing
		}

		/**
		 * The getVertexPosition() method is called to evaluate the vertex shader
		 * program that shall be executed on the GPU.
		 *
		 * @return The position of the vertex in clip space (normalized screen space).
		 *
		 */
		protected function getVertexPosition() : SFloat
		{
			throw new Error("The method 'getVertexPosition' must be implemented.");
		}

		/**
		 * The getPixelColor() method is called to evaluate the fragment shader
		 * program that shall be executed on the GPU.
		 *
		 * @return The color of the pixel on the screen.
		 *
		 */
		protected function getPixelColor() : SFloat
		{
			throw new Error("The method 'getPixelColor' must be implemented.");
		}

		private function findPass(meshBindings	: DataBindings,
								  sceneBindings	: DataBindings) : ShaderInstance
		{
			var numPasses : int = _instances.length;

			for (var passId : uint = 0; passId < numPasses; ++passId)
				if (_instances[passId].signature.isValid(meshBindings, sceneBindings))
					return _instances[passId];

			return null;
		}

		private function findOrCreateProgram(meshBindings	: DataBindings,
											 sceneBindings	: DataBindings) : Program3DResource
		{
			var numPrograms	: int = _programs.length;
			var program 	: Program3DResource;

			for (var programId : uint = 0; programId < numPrograms; ++programId)
				if (_programs[programId].signature.isValid(meshBindings, sceneBindings))
					return _programs[programId];

			var signature		: Signature		= new Signature(_name);

			_meshBindings	= new ShaderDataBindings(meshBindings, signature, Signature.SOURCE_MESH);
			_sceneBindings	= new ShaderDataBindings(sceneBindings, signature, Signature.SOURCE_SCENE);

			var vertexPosition	: AbstractNode	= getVertexPosition()._node;
			var pixelColor		: AbstractNode	= getPixelColor()._node;
			var shaderGraph		: ShaderGraph	= new ShaderGraph(vertexPosition, pixelColor, _kills);

			program			= shaderGraph.generateProgram(_name, signature);
			_meshBindings	= null;
			_sceneBindings	= null;
			_kills.length	= 0;

			_programs.push(program);

			return program;
		}

		private function findOrCreateSettings(meshBindings 	: DataBindings,
											 sceneBindings	: DataBindings) : ShaderSettings
		{
			var numConfigs	: int 				= _settings.length;
			var config		: ShaderSettings	= null;

			for (var configId : int = 0; configId < numConfigs; ++configId)
				if (_settings[configId].signature.isValid(meshBindings, sceneBindings))
					return _settings[configId];

			var signature : Signature = new Signature(_name);

			config			= _defaultSettings.clone(signature);
			_meshBindings	= new ShaderDataBindings(meshBindings, signature, Signature.SOURCE_MESH);
			_sceneBindings	= new ShaderDataBindings(sceneBindings, signature, Signature.SOURCE_SCENE);

			initializeSettings(config);

			_meshBindings	= null;
			_sceneBindings	= null;

			_settings.push(config);

			return config;
		}

		private function shaderInstanceRetainedHandler(instance : ShaderInstance,
													   numUses	: uint) : void
		{
			if (numUses == 1)
			{
				++_numActiveInstances;

				instance.begin.add(shaderInstanceBeginHandler);
				instance.end.add(shaderInstanceEndHandler);
			}
		}

		private function shaderInstanceReleasedHandler(instance : ShaderInstance,
													   numUses	: uint) : void
		{
			if (numUses == 0)
			{
				--_numActiveInstances;

				instance.begin.remove(shaderInstanceBeginHandler);
				instance.end.remove(shaderInstanceEndHandler);
			}
		}

		private function shaderInstanceBeginHandler(instance 	: ShaderInstance,
													context		: Context3DResource,
													backBuffer	: RenderTarget) : void
		{
			if (_numRenderedInstances == 0)
				_begin.execute(this, context, backBuffer);
		}

		private function shaderInstanceEndHandler(instance		: ShaderInstance,
												  context		: Context3DResource,
												  backBuffer	: RenderTarget) : void
		{
			_numRenderedInstances++;

			if (_numRenderedInstances == _numActiveInstances)
			{
				_numRenderedInstances = 0;
				_end.execute(this, context, backBuffer);
			}
		}
	}
}

fix coding style

fix coding style


package aerys.minko.render.geometry.stream.iterator
{
	import aerys.minko.ns.minko_stream;
	import aerys.minko.render.geometry.stream.IVertexStream;
	import aerys.minko.render.geometry.stream.IndexStream;

	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	/**
	 * TriangleIterator allow per-triangle access on VertexStream objects.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class TriangleIterator extends Proxy
	{
		use namespace minko_stream;

		private var _singleReference	: Boolean			= true;

		private var _offset				: int				= 0;
		private var _index				: int				= 0;

		private var _vb					: IVertexStream		= null;
		private var _ib					: IndexStream		= null;

		private var _triangle			: TriangleReference	= null;

		public function get length() : int
		{
			return _ib ? _ib.length / 3 : _vb.numVertices / 3;
		}

		public function TriangleIterator(vertexStream 		: IVertexStream,
										   indexStream		: IndexStream,
										   singleReference	: Boolean = true)
		{
			super();

			_vb = vertexStream;
			_ib = indexStream;
			_singleReference = singleReference;
		}

		override flash_proxy function hasProperty(name : *) : Boolean
		{
			return int(name) < _ib.length / 3;
		}

		override flash_proxy function nextNameIndex(index : int) : int
		{
			index -= _offset;
			_offset = 0;

			return index < _ib.length / 3 ? index + 1 : 0;
		}

		override flash_proxy function nextName(index : int) : String
		{
			return String(index - 1);
		}

		override flash_proxy function nextValue(index : int) : *
		{
			_index = index - 1;

			if (!_singleReference || !_triangle)
				_triangle = new TriangleReference(_vb, _ib, _index);

			if (_singleReference)
			{
				_triangle._index = _index;
				_triangle.v0._index = _ib._data[int(_index * 3)];
				_triangle.v1._index = _ib._data[int(_index * 3 + 1)];
				_triangle.v2._index = _ib._data[int(_index * 3 + 2)];
				_triangle._update = TriangleReference.UPDATE_ALL;
			}

			return _triangle;
		}

		override flash_proxy function getProperty(name : *) : *
		{
			return new TriangleReference(_vb, _ib, int(name));
		}

		override flash_proxy function deleteProperty(name : *) : Boolean
		{
			var index 	: uint 	= uint(name);

			_ib.deleteTriangle(index);

			return true;
		}
	}
}

package aerys.minko.render.geometry.stream.iterator
{
	import aerys.minko.ns.minko_stream;
	import aerys.minko.render.geometry.stream.IVertexStream;
	import aerys.minko.render.geometry.stream.IndexStream;

	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	/**
	 * TriangleIterator allow per-triangle access on VertexStream objects.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class TriangleIterator extends Proxy
	{
		use namespace minko_stream;

		private var _singleReference	: Boolean			= true;

		private var _offset				: int				= 0;
		private var _index				: int				= 0;

		private var _vb					: IVertexStream		= null;
		private var _ib					: IndexStream		= null;

		private var _triangle			: TriangleReference	= null;

		public function get length() : int
		{
			return _ib ? _ib.length / 3 : _vb.numVertices / 3;
		}

		public function TriangleIterator(vertexStream 		: IVertexStream,
										   indexStream		: IndexStream,
										   singleReference	: Boolean = true)
		{
			super();

			_vb = vertexStream;
			_ib = indexStream;
			_singleReference = singleReference;
		}

		override flash_proxy function hasProperty(name : *) : Boolean
		{
			return int(name) < _ib.length / 3;
		}

		override flash_proxy function nextNameIndex(index : int) : int
		{
			index -= _offset;
			_offset = 0;

			return index < _ib.length / 3 ? index + 1 : 0;
		}

		override flash_proxy function nextName(index : int) : String
		{
			return String(index - 1);
		}

		override flash_proxy function nextValue(index : int) : *
		{
			_index = index - 1;

			if (!_singleReference || !_triangle)
				_triangle = new TriangleReference(_vb, _ib, _index);

			if (_singleReference)
			{
				_triangle._index = _index;
				_triangle.v0.index = _ib.get(_index * 3);
				_triangle.v1.index = _ib.get(_index * 3 + 1);
				_triangle.v2.index = _ib.get(_index * 3 + 2);
				_triangle._update = TriangleReference.UPDATE_ALL;
			}

			return _triangle;
		}

		override flash_proxy function getProperty(name : *) : *
		{
			return new TriangleReference(_vb, _ib, int(name));
		}

		override flash_proxy function deleteProperty(name : *) : Boolean
		{
			var index 	: uint 	= uint(name);

			_ib.deleteTriangle(index);

			return true;
		}
	}
}

fix triangle iterator

fix triangle iterator


package com.threerings.util {

import flash.errors.IllegalOperationError;

import flash.utils.Dictionary;

/**
 * An implementation of a Hashtable in actionscript. Any object (and null) may
 * be used as a key. Simple keys (Number, int, uint, Boolean, String) utilize
 * a Dictionary internally for storage; keys that implement Hashable are
 * stored efficiently, and any other key can also be used if the equalsFn
 * and hashFn are specified to the constructor.
 */
public class Hashtable
    implements Map
{
    /**
     * Construct a Hashtable
     *
     * @param loadFactor - A measure of how full the hashtable is allowed to
     *                     get before it is automatically resized. The default
     *                     value of 1.75 should be fine.
     * @param equalsFn   - (Optional) A function to use to compare object
     *                     equality for keys that are neither simple nor
     *                     implement Hashable. The signature should be
     *                     "function (o1, o2) :Boolean".
     * @param hashFn     - (Optional) A function to use to generate a hash
     *                     code for keys that are neither simple nor
     *                     implement Hashable. The signature should be
     *                     "function (obj) :*", where the return type is
     *                     numeric or String. Two objects that are equals
     *                     according to the specified equalsFn *must*
     *                     generate equal values when passed to the hashFn.
     */
    public function Hashtable (
            loadFactor :Number = 1.75,
            equalsFn :Function = null,
            hashFn :Function = null)
    {
        if ((equalsFn != null) != (hashFn != null)) {
            throw new ArgumentError("Both the equals and hash functions " +
                "must be specified, or neither.");
        }
        _loadFactor = loadFactor;
        _equalsFn = equalsFn;
        _hashFn = hashFn;
    }

    // documentation inherited from interface Map
    public function clear () :void
    {
        _simpleData = null;
        _simpleSize = 0;
        _entries = null;
        _entriesSize = 0;
    }

    // documentation inherited from interface Map
    public function containsKey (key :Object) :Boolean
    {
        return (undefined !== get(key));
    }

    // documentation inherited from interface Map
    public function get (key :Object) :*
    {
        if (isSimple(key)) {
            return (_simpleData == null) ? undefined : _simpleData[key];
        }

        if (_entries == null) {
            return undefined;
        }

        var hkey :Hashable = keyFor(key);
        var hash :int = hkey.hashCode();
        var index :int = indexFor(hash);
        var e :Entry = (_entries[index] as Entry);
        while (true) {
            if (e == null) {
                return null;
            }
            if (e.hash == hash && e.key.equals(hkey)) {
                return e.value;
            }
            e = e.next;
        }
    }

    // documentation inherited from interface Map
    public function isEmpty () :Boolean
    {
        return (size() == 0);
    }

    // documentation inherited from interface Map
    public function put (key :Object, value :Object) :*
    {
        var oldValue :*;
        if (isSimple(key)) {
            if (_simpleData == null) {
                _simpleData = new Dictionary();
            }

            oldValue = _simpleData[key];
            _simpleData[key] = value;
            if (oldValue === undefined) {
                _simpleSize++;
            }
            return oldValue;
        }

        // lazy-create the array holding other hashables
        if (_entries == null) {
            _entries = new Array();
            _entries.length = DEFAULT_BUCKETS;
        }

        var hkey :Hashable = keyFor(key);
        var hash :int = hkey.hashCode();
        var index :int = indexFor(hash);
        var firstEntry :Entry = (_entries[index] as Entry);
        for (var e :Entry = firstEntry; e != null; e = e.next) {
            if (e.hash == hash && e.key.equals(hkey)) {
                oldValue = e.value;
                e.value = value;
                return oldValue; // size did not change
            }
        }

        _entries[index] = new Entry(hash, hkey, value, firstEntry);
        _entriesSize++;
        // check to see if we should grow the map
        if (_entriesSize > _entries.length * _loadFactor) {
            resize(2 * _entries.length);
        }
        // indicate that there was no value previously stored for the key
        return undefined;
    }

    // documentation inherited from interface Map
    public function remove (key :Object) :*
    {
        if (isSimple(key)) {
            if (_simpleData == null) {
                return undefined;
            }

            var oldValue :* = _simpleData[key];
            if (oldValue !== undefined) {
                _simpleSize--;
            }
            delete _simpleData[key];
            return oldValue;
        }

        if (_entries == null) {
            return undefined;
        }

        var hkey :Hashable = keyFor(key);
        var hash :int = hkey.hashCode();
        var index :int = indexFor(hash);
        var prev :Entry = (_entries[index] as Entry);
        var e :Entry = prev;

        while (e != null) {
            var next :Entry = e.next;
            if (e.hash == hash && e.key.equals(hkey)) {
                if (prev == e) {
                    _entries[index] = next;
                } else {
                    prev.next = next;
                }
                _entriesSize--;
                // check to see if we should shrink the map
                if ((_entries.length > DEFAULT_BUCKETS) &&
                        (_entriesSize < _entries.length * _loadFactor * .125)) {
                    resize(Math.max(DEFAULT_BUCKETS, _entries.length / 2));
                }
                return e.value;
            }
            prev = e;
            e = next;
        }

        return undefined; // never found
    }

    // documentation inherited from interface Map
    public function size () :int
    {
        return _simpleSize + _entriesSize;
    }

    // documentation inherited from interface Map
    public function keys () :Array
    {
        var keys :Array = new Array();

        // get the simple keys first
        if (_simpleData != null) {
            for (var key :* in _simpleData) {
                keys.push(key);
            }
        }

        // get the more complex keys
        if (_entries != null) {
            for (var ii :int = _entries.length - 1; ii >= 0; ii--) {
                for (var e :Entry = (_entries[ii] as Entry); e != null;
                        e = e.next) {
                    keys.push(e.getOriginalKey());
                }
            }
        }

        return keys;
    }

    // documentation inherited from interface Map
    public function values () :Array
    {
        var vals :Array = new Array();

        // get the simple properties first
        if (_simpleData != null) {
            for each (var value :* in _simpleData) {
                vals.push(value);
            }
        }

        // get the more complex properties
        if (_entries != null) {
            for (var ii :int = _entries.length - 1; ii >= 0; ii--) {
                for (var e :Entry = (_entries[ii] as Entry); e != null;
                        e = e.next) {
                    vals.push(e.value);
                }
            }
        }

        return vals;
    }

    // documentation inherited from interface Map
    public function forEach (fn :Function) :void
    {
        if (_simpleData != null) {
            for (var key :Object in _simpleData) {
                fn(key, _simpleData[key]);
            }
        }

        if (_entries != null) {
            for (var ii :int = _entries.length - 1; ii >= 0; ii--) {
                for (var e :Entry = (_entries[ii] as Entry); e != null;
                        e = e.next) {
                    fn(e.getOriginalKey(), e.value);
                }
            }
        }
    }

    /**
     * Return a Hashable that represents the key.
     */
    protected function keyFor (key :Object) :Hashable
    {
        if (key is Hashable) {
            return (key as Hashable);

        } else if (_hashFn == null) {
            throw new IllegalOperationError("Illegal key specified " +
                "for Hashtable created without hashing functions.");

        } else {
            return new KeyWrapper(key, _equalsFn, _hashFn);
        }
    }

    /**
     * Return an index for the specified hashcode.
     */
    protected function indexFor (hash :int) :int
    {
        // TODO: improve?
        return Math.abs(hash) % _entries.length;
    }

    /**
     * Return true if the specified key may be used to store values in a
     * Dictionary object.
     */
    protected function isSimple (key :Object) :Boolean
    {
        return (key is String) || (key is Number) || (key is Boolean);
    }

    /**
     * Resize the entries with Hashable keys to optimize
     * the memory/performance tradeoff.
     */
    protected function resize (newSize :int) :void
    {
        var oldEntries :Array = _entries;
        _entries = new Array();
        _entries.length = newSize;

        // place all the old entries in the new map
        for (var ii :int = 0; ii < oldEntries.length; ii++) {
            var e :Entry = (oldEntries[ii] as Entry);
            while (e != null) {
                var next :Entry = e.next;
                var index :int = indexFor(e.hash);
                e.next = (_entries[index] as Entry);
                _entries[index] = e;
                e = next;
            }
        }
    }

    /** The current number of key/value pairs stored in the Dictionary. */
    protected var _simpleSize :int = 0;

    /** The current number of key/value pairs stored in the _entries. */
    protected var _entriesSize :int = 0;

    /** The load factor. */
    protected var _loadFactor :Number;

    /** If non-null, contains simple key/value pairs. */
    protected var _simpleData :Dictionary

    /** If non-null, contains Hashable keys and their values. */
    protected var _entries :Array;

    /** The hashing function to use for non-Hashable complex keys. */
    protected var _hashFn :Function;

    /** The equality function to use for non-Hashable complex keys. */
    protected var _equalsFn :Function;

    /** The default size for the bucketed hashmap. */
    protected static const DEFAULT_BUCKETS :int = 16;
}

} // end: package com.threerings.util

import com.threerings.util.Hashable;

class Entry
{
    public var key :Hashable;
    public var value :Object;
    public var hash :int;
    public var next :Entry;

    public function Entry (hash :int, key :Hashable, value :Object, next :Entry)
    {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

    /**
     * Get the original key used to store this entry.
     */
    public function getOriginalKey () :Object
    {
        if (key is KeyWrapper) {
            return (key as KeyWrapper).key;
        }
        return key;
    }
}

class KeyWrapper
    implements Hashable
{
    public var key :Object;

    public function KeyWrapper (
            key :Object, equalsFn :Function, hashFn :Function)
    {
        this.key = key;
        _equalsFn = equalsFn;
        var hashValue :* = hashFn(key);
        if (hashValue is String) {
            var uid :String = (hashValue as String);
            // examine at most 32 characters of the string
            var inc :int = int(Math.max(1, Math.ceil(uid.length / 32)));
            for (var ii :int = 0; ii < uid.length; ii += inc) {
                _hash = (_hash << 1) ^ int(uid.charCodeAt(ii));
            }

        } else {
            _hash = int(hashValue);
        }
    }

    // documentation inherited from interface Hashable
    public function equals (other :Object) :Boolean
    {
        return (other is KeyWrapper) &&
            Boolean(_equalsFn(key, (other as KeyWrapper).key));
    }

    // documentation inherited from interface Hashable
    public function hashCode () :int
    {
        return _hash;
    }

    protected var _key :Object;
    protected var _hash :int;
    protected var _equalsFn :Function;
}

package com.threerings.util {

import flash.errors.IllegalOperationError;

import flash.utils.Dictionary;

/**
 * An implementation of a Hashtable in actionscript. Any object (and null) may
 * be used as a key. Simple keys (Number, int, uint, Boolean, String) utilize
 * a Dictionary internally for storage; keys that implement Hashable are
 * stored efficiently, and any other key can also be used if the equalsFn
 * and hashFn are specified to the constructor.
 */
public class Hashtable
    implements Map
{
    /**
     * Construct a Hashtable
     *
     * @param loadFactor - A measure of how full the hashtable is allowed to
     *                     get before it is automatically resized. The default
     *                     value of 1.75 should be fine.
     * @param equalsFn   - (Optional) A function to use to compare object
     *                     equality for keys that are neither simple nor
     *                     implement Hashable. The signature should be
     *                     "function (o1, o2) :Boolean".
     * @param hashFn     - (Optional) A function to use to generate a hash
     *                     code for keys that are neither simple nor
     *                     implement Hashable. The signature should be
     *                     "function (obj) :*", where the return type is
     *                     numeric or String. Two objects that are equals
     *                     according to the specified equalsFn *must*
     *                     generate equal values when passed to the hashFn.
     */
    public function Hashtable (
            loadFactor :Number = 1.75,
            equalsFn :Function = null,
            hashFn :Function = null)
    {
        if ((equalsFn != null) != (hashFn != null)) {
            throw new ArgumentError("Both the equals and hash functions " +
                "must be specified, or neither.");
        }
        _loadFactor = loadFactor;
        _equalsFn = equalsFn;
        _hashFn = hashFn;
    }

    // documentation inherited from interface Map
    public function clear () :void
    {
        _simpleData = null;
        _simpleSize = 0;
        _entries = null;
        _entriesSize = 0;
    }

    // documentation inherited from interface Map
    public function containsKey (key :Object) :Boolean
    {
        return (undefined !== get(key));
    }

    // documentation inherited from interface Map
    public function get (key :Object) :*
    {
        if (isSimple(key)) {
            return (_simpleData == null) ? undefined : _simpleData[key];
        }

        if (_entries == null) {
            return undefined;
        }

        var hkey :Hashable = keyFor(key);
        var hash :int = hkey.hashCode();
        var index :int = indexFor(hash);
        var e :Entry = (_entries[index] as Entry);
        while (true) {
            if (e == null) {
                return undefined;
            }
            if (e.hash == hash && e.key.equals(hkey)) {
                return e.value;
            }
            e = e.next;
        }
    }

    // documentation inherited from interface Map
    public function isEmpty () :Boolean
    {
        return (size() == 0);
    }

    // documentation inherited from interface Map
    public function put (key :Object, value :Object) :*
    {
        var oldValue :*;
        if (isSimple(key)) {
            if (_simpleData == null) {
                _simpleData = new Dictionary();
            }

            oldValue = _simpleData[key];
            _simpleData[key] = value;
            if (oldValue === undefined) {
                _simpleSize++;
            }
            return oldValue;
        }

        // lazy-create the array holding other hashables
        if (_entries == null) {
            _entries = new Array();
            _entries.length = DEFAULT_BUCKETS;
        }

        var hkey :Hashable = keyFor(key);
        var hash :int = hkey.hashCode();
        var index :int = indexFor(hash);
        var firstEntry :Entry = (_entries[index] as Entry);
        for (var e :Entry = firstEntry; e != null; e = e.next) {
            if (e.hash == hash && e.key.equals(hkey)) {
                oldValue = e.value;
                e.value = value;
                return oldValue; // size did not change
            }
        }

        _entries[index] = new Entry(hash, hkey, value, firstEntry);
        _entriesSize++;
        // check to see if we should grow the map
        if (_entriesSize > _entries.length * _loadFactor) {
            resize(2 * _entries.length);
        }
        // indicate that there was no value previously stored for the key
        return undefined;
    }

    // documentation inherited from interface Map
    public function remove (key :Object) :*
    {
        if (isSimple(key)) {
            if (_simpleData == null) {
                return undefined;
            }

            var oldValue :* = _simpleData[key];
            if (oldValue !== undefined) {
                _simpleSize--;
            }
            delete _simpleData[key];
            return oldValue;
        }

        if (_entries == null) {
            return undefined;
        }

        var hkey :Hashable = keyFor(key);
        var hash :int = hkey.hashCode();
        var index :int = indexFor(hash);
        var prev :Entry = (_entries[index] as Entry);
        var e :Entry = prev;

        while (e != null) {
            var next :Entry = e.next;
            if (e.hash == hash && e.key.equals(hkey)) {
                if (prev == e) {
                    _entries[index] = next;
                } else {
                    prev.next = next;
                }
                _entriesSize--;
                // check to see if we should shrink the map
                if ((_entries.length > DEFAULT_BUCKETS) &&
                        (_entriesSize < _entries.length * _loadFactor * .125)) {
                    resize(Math.max(DEFAULT_BUCKETS, _entries.length / 2));
                }
                return e.value;
            }
            prev = e;
            e = next;
        }

        return undefined; // never found
    }

    // documentation inherited from interface Map
    public function size () :int
    {
        return _simpleSize + _entriesSize;
    }

    // documentation inherited from interface Map
    public function keys () :Array
    {
        var keys :Array = new Array();

        // get the simple keys first
        if (_simpleData != null) {
            for (var key :* in _simpleData) {
                keys.push(key);
            }
        }

        // get the more complex keys
        if (_entries != null) {
            for (var ii :int = _entries.length - 1; ii >= 0; ii--) {
                for (var e :Entry = (_entries[ii] as Entry); e != null;
                        e = e.next) {
                    keys.push(e.getOriginalKey());
                }
            }
        }

        return keys;
    }

    // documentation inherited from interface Map
    public function values () :Array
    {
        var vals :Array = new Array();

        // get the simple properties first
        if (_simpleData != null) {
            for each (var value :* in _simpleData) {
                vals.push(value);
            }
        }

        // get the more complex properties
        if (_entries != null) {
            for (var ii :int = _entries.length - 1; ii >= 0; ii--) {
                for (var e :Entry = (_entries[ii] as Entry); e != null;
                        e = e.next) {
                    vals.push(e.value);
                }
            }
        }

        return vals;
    }

    // documentation inherited from interface Map
    public function forEach (fn :Function) :void
    {
        if (_simpleData != null) {
            for (var key :Object in _simpleData) {
                fn(key, _simpleData[key]);
            }
        }

        if (_entries != null) {
            for (var ii :int = _entries.length - 1; ii >= 0; ii--) {
                for (var e :Entry = (_entries[ii] as Entry); e != null;
                        e = e.next) {
                    fn(e.getOriginalKey(), e.value);
                }
            }
        }
    }

    /**
     * Return a Hashable that represents the key.
     */
    protected function keyFor (key :Object) :Hashable
    {
        if (key is Hashable) {
            return (key as Hashable);

        } else if (_hashFn == null) {
            throw new IllegalOperationError("Illegal key specified " +
                "for Hashtable created without hashing functions.");

        } else {
            return new KeyWrapper(key, _equalsFn, _hashFn);
        }
    }

    /**
     * Return an index for the specified hashcode.
     */
    protected function indexFor (hash :int) :int
    {
        // TODO: improve?
        return Math.abs(hash) % _entries.length;
    }

    /**
     * Return true if the specified key may be used to store values in a
     * Dictionary object.
     */
    protected function isSimple (key :Object) :Boolean
    {
        return (key is String) || (key is Number) || (key is Boolean);
    }

    /**
     * Resize the entries with Hashable keys to optimize
     * the memory/performance tradeoff.
     */
    protected function resize (newSize :int) :void
    {
        var oldEntries :Array = _entries;
        _entries = new Array();
        _entries.length = newSize;

        // place all the old entries in the new map
        for (var ii :int = 0; ii < oldEntries.length; ii++) {
            var e :Entry = (oldEntries[ii] as Entry);
            while (e != null) {
                var next :Entry = e.next;
                var index :int = indexFor(e.hash);
                e.next = (_entries[index] as Entry);
                _entries[index] = e;
                e = next;
            }
        }
    }

    /** The current number of key/value pairs stored in the Dictionary. */
    protected var _simpleSize :int = 0;

    /** The current number of key/value pairs stored in the _entries. */
    protected var _entriesSize :int = 0;

    /** The load factor. */
    protected var _loadFactor :Number;

    /** If non-null, contains simple key/value pairs. */
    protected var _simpleData :Dictionary

    /** If non-null, contains Hashable keys and their values. */
    protected var _entries :Array;

    /** The hashing function to use for non-Hashable complex keys. */
    protected var _hashFn :Function;

    /** The equality function to use for non-Hashable complex keys. */
    protected var _equalsFn :Function;

    /** The default size for the bucketed hashmap. */
    protected static const DEFAULT_BUCKETS :int = 16;
}

} // end: package com.threerings.util

import com.threerings.util.Hashable;

class Entry
{
    public var key :Hashable;
    public var value :Object;
    public var hash :int;
    public var next :Entry;

    public function Entry (hash :int, key :Hashable, value :Object, next :Entry)
    {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

    /**
     * Get the original key used to store this entry.
     */
    public function getOriginalKey () :Object
    {
        if (key is KeyWrapper) {
            return (key as KeyWrapper).key;
        }
        return key;
    }
}

class KeyWrapper
    implements Hashable
{
    public var key :Object;

    public function KeyWrapper (
            key :Object, equalsFn :Function, hashFn :Function)
    {
        this.key = key;
        _equalsFn = equalsFn;
        var hashValue :* = hashFn(key);
        if (hashValue is String) {
            var uid :String = (hashValue as String);
            // examine at most 32 characters of the string
            var inc :int = int(Math.max(1, Math.ceil(uid.length / 32)));
            for (var ii :int = 0; ii < uid.length; ii += inc) {
                _hash = (_hash << 1) ^ int(uid.charCodeAt(ii));
            }

        } else {
            _hash = int(hashValue);
        }
    }

    // documentation inherited from interface Hashable
    public function equals (other :Object) :Boolean
    {
        return (other is KeyWrapper) &&
            Boolean(_equalsFn(key, (other as KeyWrapper).key));
    }

    // documentation inherited from interface Hashable
    public function hashCode () :int
    {
        return _hash;
    }

    protected var _key :Object;
    protected var _hash :int;
    protected var _equalsFn :Function;
}

Bugfix: return undefined, not null, to indicate absence in the map.

git-svn-id: a1a4b28b82a3276cc491891159dd9963a0a72fae@4386 542714f4-19e9-0310-aa3c-eee0fc999fb1


package battlecode.client.viewer.render {
    import battlecode.client.viewer.MatchController;
    import battlecode.common.MapLocation;
    import battlecode.common.Team;
    import battlecode.common.TerrainTile;
    import battlecode.events.MatchEvent;
    import battlecode.world.GameMap;

    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.ColorTransform;
    import flash.geom.Rectangle;

    import mx.containers.Canvas;
    import mx.core.UIComponent;

    public class DrawMap extends Canvas {
        private var controller:MatchController;
        private var origin:MapLocation;

        // various canvases for layering and quick toggling of features
        private var mapCanvas:UIComponent;
        private var gridCanvas:UIComponent;
        private var rubbleCanvas:UIComponent;
        private var partsCanvas:UIComponent;
        private var groundUnitCanvas:UIComponent;
        private var zombieUnitCanvas:UIComponent;

        // optimizations for caching
        private var lastRound:uint = 0;

        public function DrawMap(controller:MatchController) {
            super();

            this.controller = controller;
            this.controller.addEventListener(MatchEvent.ROUND_CHANGE, onRoundChange);
            this.controller.addEventListener(MatchEvent.MATCH_CHANGE, onMatchChange);
            this.addEventListener(Event.ENTER_FRAME, onEnterFrame);

            this.mapCanvas = new UIComponent();
            this.gridCanvas = new UIComponent();
            this.rubbleCanvas = new UIComponent();
            this.partsCanvas = new UIComponent();
            this.groundUnitCanvas = new UIComponent();
            this.zombieUnitCanvas = new UIComponent();

            this.mapCanvas.cacheAsBitmap = true;
            this.gridCanvas.cacheAsBitmap = true;

            this.addChild(mapCanvas);
            this.addChild(rubbleCanvas);
            this.addChild(partsCanvas);
            this.addChild(gridCanvas);
            this.addChild(groundUnitCanvas);
            this.addChild(zombieUnitCanvas);
        }

        ///////////////////////////////////////////////////////
        ////////////////// UTILITY METHODS ////////////////////
        ///////////////////////////////////////////////////////

        public function getMapWidth():uint {
            if (controller.match)
                return controller.match.getMap().getWidth() * RenderConfiguration.GRID_SIZE;
            return 0;
        }

        public function getMapHeight():uint {
            if (controller.match)
                return controller.match.getMap().getHeight() * RenderConfiguration.GRID_SIZE + 5;
            return 0;
        }

        private function getGridSize():Number {
            return RenderConfiguration.getGridSize();
        }

        ///////////////////////////////////////////////////////
        ////////////////// DRAWING METHODS ////////////////////
        ///////////////////////////////////////////////////////

        public function redrawAll():void {
            drawMap();
            drawGridlines();
            drawUnits();
            drawRubble();
            drawParts();

            var o:DrawObject;

            for each (o in controller.currentState.getGroundRobots()) {
                o.draw(true);
            }

            this.scrollRect = new Rectangle(this.mapCanvas.x, this.mapCanvas.y,
                    getMapWidth() * RenderConfiguration.getScalingFactor(),
                    getMapHeight() * RenderConfiguration.getScalingFactor());
        }

        private function drawMap():void {
            var i:uint, j:uint, tile:TerrainTile;
            var map:GameMap = controller.match.getMap();
            var terrain:Array = map.getTerrainTiles();
            var colorTransform:ColorTransform, scalar:uint;
            var g:Number = getGridSize();

            origin = map.getOrigin();

            this.mapCanvas.graphics.clear();

            // draw terrain
            for (i = 0; i < map.getHeight(); i++) {
                for (j = 0; j < map.getWidth(); j++) {
                    tile = terrain[i][j] as TerrainTile;
                    if (tile.getType() == TerrainTile.LAND) {
                        scalar = 0xFF * 0.9;
                        colorTransform = new ColorTransform(0, 0, 0, 1, scalar, scalar, scalar, 0);
                        this.mapCanvas.graphics.beginFill(colorTransform.color, 1.0);
                        this.mapCanvas.graphics.drawRect(j * g, i * g, g, g);
                        this.mapCanvas.graphics.endFill();
                    } else {
                        colorTransform = new ColorTransform(0, 0, 0, 1, 0x00, 0x00, 0x99, 0);
                        this.mapCanvas.graphics.beginFill(colorTransform.color, 1.0);
                        this.mapCanvas.graphics.drawRect(j * g, i * g, g, g);
                        this.mapCanvas.graphics.endFill();
                    }
                }
            }

            // draw void outlines
            for (i = 0; i < map.getHeight(); i++) {
                for (j = 0; j < map.getWidth(); j++) {
                    tile = terrain[i][j] as TerrainTile;
                    if (tile.getType() == TerrainTile.VOID) {
                        this.mapCanvas.graphics.lineStyle(2, 0xFFFFFF);
                        if (i > 0 && terrain[i-1][j].getType() == TerrainTile.LAND) {
                            this.mapCanvas.graphics.moveTo(j * g, i * g);
                            this.mapCanvas.graphics.lineTo((j + 1) * g, i * g);
                        }
                        if (j > 0 && terrain[i][j-1].getType() == TerrainTile.LAND) {
                            this.mapCanvas.graphics.moveTo(j * g, i * g);
                            this.mapCanvas.graphics.lineTo(j * g, (i + 1) * g);
                        }
                        if (i < map.getHeight() - 1 && terrain[i+1][j].getType() == TerrainTile.LAND) {
                            this.mapCanvas.graphics.moveTo(j * g, (i + 1) * g);
                            this.mapCanvas.graphics.lineTo((j + 1) * g, (i + 1) * g);
                        }
                        if (j < map.getWidth() - 1 && terrain[i][j+1].getType() == TerrainTile.LAND) {
                            this.mapCanvas.graphics.moveTo((j + 1) * g, i * g);
                            this.mapCanvas.graphics.lineTo((j + 1) * g, (i + 1) * g);
                        }
                        this.mapCanvas.graphics.lineStyle();
                    }
                }
            }
        }

        private function drawGridlines():void {
            var i:uint, j:uint;
            var map:GameMap = controller.match.getMap();

            this.gridCanvas.graphics.clear();
            for (i = 0; i < map.getHeight(); i++) {
                for (j = 0; j < map.getWidth(); j++) {
                    this.gridCanvas.graphics.lineStyle(.5, 0x999999, 0.3);
                    this.gridCanvas.graphics.drawRect(j * getGridSize(), i * getGridSize(), getGridSize(), getGridSize());
                }
            }
        }

        private function drawRubble():void {
            var terrain:Array = controller.match.getMap().getTerrainTiles();
            var rubble:Array = controller.currentState.getRubble();
            var i:uint, j:uint;

            this.rubbleCanvas.graphics.clear();
            this.rubbleCanvas.graphics.lineStyle();
            var g:Number = getGridSize();
            for (i = 0; i < rubble.length; i++) {
                for (j = 0; j < rubble[i].length; j++) {
                    var tile:TerrainTile = terrain[i][j] as TerrainTile;
                    if (tile.getType() == TerrainTile.VOID) {
                        continue;
                    }
                    var density:Number = Math.min(1, rubble[i][j] / 100);
                    if (density <= 0) {
                        continue;
                    }

                    // grey
                    var scalarR:uint = (1 - density) * 0xFF;
                    var scalarG:uint = (1 - density) * 0xFF;
                    var scalarB:uint = (1 - density) * 0xFF;

                    var colorTransform:ColorTransform = new ColorTransform(0, 0, 0, 1, scalarR, scalarG, scalarB, 0);
                    this.rubbleCanvas.graphics.beginFill(colorTransform.color, 1.0);
                    this.rubbleCanvas.graphics.drawRect(j * g, i * g, g, g);
                    this.rubbleCanvas.graphics.endFill();
                }
            }
        }

        private function drawParts():void {
            var terrain:Array = controller.match.getMap().getTerrainTiles();
            var parts:Array = controller.currentState.getParts();
            var i:uint, j:uint;

            this.partsCanvas.graphics.clear();
            this.partsCanvas.graphics.lineStyle();
            var g:Number = getGridSize();
            for (i = 0; i < parts.length; i++) {
                for (j = 0; j < parts[i].length; j++) {
                    var tile:TerrainTile = terrain[i][j] as TerrainTile;
                    if (tile.getType() == TerrainTile.VOID) {
                        continue;
                    }
                    var density:Number = Math.min(1, parts[i][j] / 200);
                    if (density <= 0) {
                        continue;
                    }

                    this.partsCanvas.graphics.beginFill(0xCC33CC, 0.7);
                    this.partsCanvas.graphics.drawCircle(j * g + g / 2, i * g + g / 2, density * g / 2);
                    this.partsCanvas.graphics.endFill();
                }
            }
        }

        private function drawUnits():void {
            var loc:MapLocation, i:uint, j:uint, robot:DrawRobot;
            var groundRobots:Object = controller.currentState.getGroundRobots();
            var zombieRobots:Object = controller.currentState.getZombieRobots();

            while (groundUnitCanvas.numChildren > 0)
                groundUnitCanvas.removeChildAt(0);

            while (zombieUnitCanvas.numChildren > 0)
                zombieUnitCanvas.removeChildAt(0);

            for each (robot in groundRobots) {
                loc = robot.getLocation();
                j = (loc.getX() - origin.getX());
                i = (loc.getY() - origin.getY());
                robot.x = j * getGridSize() + getGridSize() / 2;
                robot.y = i * getGridSize() + getGridSize() / 2;
                robot.addEventListener(MouseEvent.CLICK, onRobotSelect, false, 0, true);
                groundUnitCanvas.addChild(robot);
                robot.draw();
            }

            for each (robot in zombieRobots) {
                loc = robot.getLocation();
                j = (loc.getX() - origin.getX());
                i = (loc.getY() - origin.getY());
                robot.x = j * getGridSize() + getGridSize() / 2;
                robot.y = i * getGridSize() + getGridSize() / 2;
                robot.addEventListener(MouseEvent.CLICK, onRobotSelect, false, 0, true);
                zombieUnitCanvas.addChild(robot);
                robot.draw();
            }
        }

        private function updateUnits():void {
            var loc:MapLocation, i:uint, j:uint, robot:DrawRobot;
            var groundRobots:Object = controller.currentState.getGroundRobots();
            var zombieRobots:Object = controller.currentState.getZombieRobots();

            for each (robot in groundRobots) {
                loc = robot.getLocation();
                j = (loc.getX() - origin.getX());
                i = (loc.getY() - origin.getY());
                robot.x = j * getGridSize() + getGridSize() / 2;
                robot.y = i * getGridSize() + getGridSize() / 2;
                if (!robot.parent && robot.isAlive()) {
                    robot.addEventListener(MouseEvent.CLICK, onRobotSelect, false, 0, true);
                    groundUnitCanvas.addChild(robot);
                }
                robot.draw();
            }

            for each (robot in zombieRobots) {
                loc = robot.getLocation();
                j = (loc.getX() - origin.getX());
                i = (loc.getY() - origin.getY());
                robot.x = j * getGridSize() + getGridSize() / 2;
                robot.y = i * getGridSize() + getGridSize() / 2;
                if (!robot.parent && robot.isAlive()) {
                    robot.addEventListener(MouseEvent.CLICK, onRobotSelect, false, 0, true);
                    zombieUnitCanvas.addChild(robot);
                }
                robot.draw();
            }
        }

        ///////////////////////////////////////////////////////
        //////////////////// EVENT HANDLERS ///////////////////
        ///////////////////////////////////////////////////////

        private function onEnterFrame(e:Event):void {
            gridCanvas.visible = RenderConfiguration.showGridlines();
            groundUnitCanvas.visible = RenderConfiguration.showGround();
            zombieUnitCanvas.visible = RenderConfiguration.showGround(); // TODO showZombies
            partsCanvas.visible = RenderConfiguration.showRubble(); // TODO showParts
            rubbleCanvas.visible = RenderConfiguration.showRubble();
        }

        private function onRoundChange(e:MatchEvent):void {
            if (e.currentRound < lastRound) {
                drawUnits();
            }
            updateUnits();
            drawRubble();
            drawParts();

            lastRound = e.currentRound;

            this.visible = (e.currentRound != 0);
        }

        private function onMatchChange(e:MatchEvent):void {
            redrawAll();
        }

        private function onRobotSelect(e:MouseEvent):void {
            var x:Number, y:Number;

            if (RenderConfiguration.isTournament())
                return;

            if (controller.selectedRobot && controller.selectedRobot.parent) {
                controller.selectedRobot.setSelected(false);
                x = controller.selectedRobot.x;
                y = controller.selectedRobot.y;
                controller.selectedRobot.draw(true);
                controller.selectedRobot.x = x;
                controller.selectedRobot.y = y;
            }

            var robot:DrawRobot = e.currentTarget as DrawRobot;
            robot.setSelected(true);
            x = robot.x;
            y = robot.y;
            robot.draw(true);
            robot.x = x;
            robot.y = y;
            controller.selectedRobot = robot;
        }

    }

}

package battlecode.client.viewer.render {
    import battlecode.client.viewer.MatchController;
    import battlecode.common.MapLocation;
    import battlecode.common.Team;
    import battlecode.common.TerrainTile;
    import battlecode.events.MatchEvent;
    import battlecode.world.GameMap;

    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.ColorTransform;
    import flash.geom.Rectangle;

    import mx.containers.Canvas;
    import mx.core.UIComponent;

    public class DrawMap extends Canvas {
        private var controller:MatchController;
        private var origin:MapLocation;

        // various canvases for layering and quick toggling of features
        private var mapCanvas:UIComponent;
        private var gridCanvas:UIComponent;
        private var rubbleCanvas:UIComponent;
        private var partsCanvas:UIComponent;
        private var groundUnitCanvas:UIComponent;
        private var zombieUnitCanvas:UIComponent;

        // optimizations for caching
        private var lastRound:uint = 0;

        public function DrawMap(controller:MatchController) {
            super();

            this.controller = controller;
            this.controller.addEventListener(MatchEvent.ROUND_CHANGE, onRoundChange);
            this.controller.addEventListener(MatchEvent.MATCH_CHANGE, onMatchChange);
            this.addEventListener(Event.ENTER_FRAME, onEnterFrame);

            this.mapCanvas = new UIComponent();
            this.gridCanvas = new UIComponent();
            this.rubbleCanvas = new UIComponent();
            this.partsCanvas = new UIComponent();
            this.groundUnitCanvas = new UIComponent();
            this.zombieUnitCanvas = new UIComponent();

            this.mapCanvas.cacheAsBitmap = true;
            this.gridCanvas.cacheAsBitmap = true;

            this.addChild(mapCanvas);
            this.addChild(rubbleCanvas);
            this.addChild(partsCanvas);
            this.addChild(gridCanvas);
            this.addChild(groundUnitCanvas);
            this.addChild(zombieUnitCanvas);
        }

        ///////////////////////////////////////////////////////
        ////////////////// UTILITY METHODS ////////////////////
        ///////////////////////////////////////////////////////

        public function getMapWidth():uint {
            if (controller.match)
                return controller.match.getMap().getWidth() * RenderConfiguration.GRID_SIZE;
            return 0;
        }

        public function getMapHeight():uint {
            if (controller.match)
                return controller.match.getMap().getHeight() * RenderConfiguration.GRID_SIZE + 5;
            return 0;
        }

        private function getGridSize():Number {
            return RenderConfiguration.getGridSize();
        }

        ///////////////////////////////////////////////////////
        ////////////////// DRAWING METHODS ////////////////////
        ///////////////////////////////////////////////////////

        public function redrawAll():void {
            drawMap();
            drawGridlines();
            drawUnits();
            drawRubble();
            drawParts();

            var o:DrawObject;

            for each (o in controller.currentState.getGroundRobots()) {
                o.draw(true);
            }

            this.scrollRect = new Rectangle(this.mapCanvas.x, this.mapCanvas.y,
                    getMapWidth() * RenderConfiguration.getScalingFactor(),
                    getMapHeight() * RenderConfiguration.getScalingFactor());
        }

        private function drawMap():void {
            var i:uint, j:uint, tile:TerrainTile;
            var map:GameMap = controller.match.getMap();
            var terrain:Array = map.getTerrainTiles();
            var colorTransform:ColorTransform, scalar:uint;
            var g:Number = getGridSize();

            origin = map.getOrigin();

            this.mapCanvas.graphics.clear();

            // draw terrain
            for (i = 0; i < map.getHeight(); i++) {
                for (j = 0; j < map.getWidth(); j++) {
                    tile = terrain[i][j] as TerrainTile;
                    if (tile.getType() == TerrainTile.LAND) {
                        scalar = 0xFF * 0.9;
                        colorTransform = new ColorTransform(0, 0, 0, 1, scalar, scalar, scalar, 0);
                        this.mapCanvas.graphics.beginFill(colorTransform.color, 1.0);
                        this.mapCanvas.graphics.drawRect(j * g, i * g, g, g);
                        this.mapCanvas.graphics.endFill();
                    } else {
                        colorTransform = new ColorTransform(0, 0, 0, 1, 0x00, 0x00, 0x99, 0);
                        this.mapCanvas.graphics.beginFill(colorTransform.color, 1.0);
                        this.mapCanvas.graphics.drawRect(j * g, i * g, g, g);
                        this.mapCanvas.graphics.endFill();
                    }
                }
            }

            // draw void outlines
            for (i = 0; i < map.getHeight(); i++) {
                for (j = 0; j < map.getWidth(); j++) {
                    tile = terrain[i][j] as TerrainTile;
                    if (tile.getType() == TerrainTile.VOID) {
                        this.mapCanvas.graphics.lineStyle(2, 0xFFFFFF);
                        if (i > 0 && terrain[i-1][j].getType() == TerrainTile.LAND) {
                            this.mapCanvas.graphics.moveTo(j * g, i * g);
                            this.mapCanvas.graphics.lineTo((j + 1) * g, i * g);
                        }
                        if (j > 0 && terrain[i][j-1].getType() == TerrainTile.LAND) {
                            this.mapCanvas.graphics.moveTo(j * g, i * g);
                            this.mapCanvas.graphics.lineTo(j * g, (i + 1) * g);
                        }
                        if (i < map.getHeight() - 1 && terrain[i+1][j].getType() == TerrainTile.LAND) {
                            this.mapCanvas.graphics.moveTo(j * g, (i + 1) * g);
                            this.mapCanvas.graphics.lineTo((j + 1) * g, (i + 1) * g);
                        }
                        if (j < map.getWidth() - 1 && terrain[i][j+1].getType() == TerrainTile.LAND) {
                            this.mapCanvas.graphics.moveTo((j + 1) * g, i * g);
                            this.mapCanvas.graphics.lineTo((j + 1) * g, (i + 1) * g);
                        }
                        this.mapCanvas.graphics.lineStyle();
                    }
                }
            }
        }

        private function drawGridlines():void {
            var i:uint, j:uint;
            var map:GameMap = controller.match.getMap();

            this.gridCanvas.graphics.clear();
            for (i = 0; i < map.getHeight(); i++) {
                for (j = 0; j < map.getWidth(); j++) {
                    this.gridCanvas.graphics.lineStyle(.5, 0x999999, 0.3);
                    this.gridCanvas.graphics.drawRect(j * getGridSize(), i * getGridSize(), getGridSize(), getGridSize());
                }
            }
        }

        private function drawRubble():void {
            var terrain:Array = controller.match.getMap().getTerrainTiles();
            var rubble:Array = controller.currentState.getRubble();
            var i:uint, j:uint;

            this.rubbleCanvas.graphics.clear();
            this.rubbleCanvas.graphics.lineStyle();
            var g:Number = getGridSize();
            for (i = 0; i < rubble.length; i++) {
                for (j = 0; j < rubble[i].length; j++) {
                    var tile:TerrainTile = terrain[i][j] as TerrainTile;
                    if (tile.getType() == TerrainTile.VOID) {
                        continue;
                    }

                    var density:Number = Math.min(1, rubble[i][j] / 100);
                    if (density <= .5) {
                        continue;
                    }

                    var color:uint = density < 1 ? 0x999999 : 0x000000;
                    this.rubbleCanvas.graphics.beginFill(color, 1.0);
                    this.rubbleCanvas.graphics.drawRect(j * g, i * g, g, g);
                    this.rubbleCanvas.graphics.endFill();
                }
            }
        }

        private function drawParts():void {
            var terrain:Array = controller.match.getMap().getTerrainTiles();
            var parts:Array = controller.currentState.getParts();
            var i:uint, j:uint;

            this.partsCanvas.graphics.clear();
            this.partsCanvas.graphics.lineStyle();
            var g:Number = getGridSize();
            for (i = 0; i < parts.length; i++) {
                for (j = 0; j < parts[i].length; j++) {
                    var tile:TerrainTile = terrain[i][j] as TerrainTile;
                    if (tile.getType() == TerrainTile.VOID) {
                        continue;
                    }
                    var density:Number = Math.min(1, parts[i][j] / 200);
                    if (density <= 0) {
                        continue;
                    }

                    this.partsCanvas.graphics.beginFill(0xCC33CC, 0.7);
                    this.partsCanvas.graphics.drawCircle(j * g + g / 2, i * g + g / 2, density * g / 2);
                    this.partsCanvas.graphics.endFill();
                }
            }
        }

        private function drawUnits():void {
            var loc:MapLocation, i:uint, j:uint, robot:DrawRobot;
            var groundRobots:Object = controller.currentState.getGroundRobots();
            var zombieRobots:Object = controller.currentState.getZombieRobots();

            while (groundUnitCanvas.numChildren > 0)
                groundUnitCanvas.removeChildAt(0);

            while (zombieUnitCanvas.numChildren > 0)
                zombieUnitCanvas.removeChildAt(0);

            for each (robot in groundRobots) {
                loc = robot.getLocation();
                j = (loc.getX() - origin.getX());
                i = (loc.getY() - origin.getY());
                robot.x = j * getGridSize() + getGridSize() / 2;
                robot.y = i * getGridSize() + getGridSize() / 2;
                robot.addEventListener(MouseEvent.CLICK, onRobotSelect, false, 0, true);
                groundUnitCanvas.addChild(robot);
                robot.draw();
            }

            for each (robot in zombieRobots) {
                loc = robot.getLocation();
                j = (loc.getX() - origin.getX());
                i = (loc.getY() - origin.getY());
                robot.x = j * getGridSize() + getGridSize() / 2;
                robot.y = i * getGridSize() + getGridSize() / 2;
                robot.addEventListener(MouseEvent.CLICK, onRobotSelect, false, 0, true);
                zombieUnitCanvas.addChild(robot);
                robot.draw();
            }
        }

        private function updateUnits():void {
            var loc:MapLocation, i:uint, j:uint, robot:DrawRobot;
            var groundRobots:Object = controller.currentState.getGroundRobots();
            var zombieRobots:Object = controller.currentState.getZombieRobots();

            for each (robot in groundRobots) {
                loc = robot.getLocation();
                j = (loc.getX() - origin.getX());
                i = (loc.getY() - origin.getY());
                robot.x = j * getGridSize() + getGridSize() / 2;
                robot.y = i * getGridSize() + getGridSize() / 2;
                if (!robot.parent && robot.isAlive()) {
                    robot.addEventListener(MouseEvent.CLICK, onRobotSelect, false, 0, true);
                    groundUnitCanvas.addChild(robot);
                }
                robot.draw();
            }

            for each (robot in zombieRobots) {
                loc = robot.getLocation();
                j = (loc.getX() - origin.getX());
                i = (loc.getY() - origin.getY());
                robot.x = j * getGridSize() + getGridSize() / 2;
                robot.y = i * getGridSize() + getGridSize() / 2;
                if (!robot.parent && robot.isAlive()) {
                    robot.addEventListener(MouseEvent.CLICK, onRobotSelect, false, 0, true);
                    zombieUnitCanvas.addChild(robot);
                }
                robot.draw();
            }
        }

        ///////////////////////////////////////////////////////
        //////////////////// EVENT HANDLERS ///////////////////
        ///////////////////////////////////////////////////////

        private function onEnterFrame(e:Event):void {
            gridCanvas.visible = RenderConfiguration.showGridlines();
            groundUnitCanvas.visible = RenderConfiguration.showGround();
            zombieUnitCanvas.visible = RenderConfiguration.showGround(); // TODO showZombies
            partsCanvas.visible = RenderConfiguration.showRubble(); // TODO showParts
            rubbleCanvas.visible = RenderConfiguration.showRubble();
        }

        private function onRoundChange(e:MatchEvent):void {
            if (e.currentRound < lastRound) {
                drawUnits();
            }
            updateUnits();
            drawRubble();
            drawParts();

            lastRound = e.currentRound;

            this.visible = (e.currentRound != 0);
        }

        private function onMatchChange(e:MatchEvent):void {
            redrawAll();
        }

        private function onRobotSelect(e:MouseEvent):void {
            var x:Number, y:Number;

            if (RenderConfiguration.isTournament())
                return;

            if (controller.selectedRobot && controller.selectedRobot.parent) {
                controller.selectedRobot.setSelected(false);
                x = controller.selectedRobot.x;
                y = controller.selectedRobot.y;
                controller.selectedRobot.draw(true);
                controller.selectedRobot.x = x;
                controller.selectedRobot.y = y;
            }

            var robot:DrawRobot = e.currentTarget as DrawRobot;
            robot.setSelected(true);
            x = robot.x;
            y = robot.y;
            robot.draw(true);
            robot.x = x;
            robot.y = y;
            controller.selectedRobot = robot;
        }

    }

}

make rubble easier to see

make rubble easier to see