In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

2D Water + Refraction (maybe)

Author: Richard Owen
Based on some code i saw along time ago and I can not find now to reference
Move your mouse around the stage and check out the water trail
this works well as rain drops if you want to randomly drop points onto the surface
Hi Richard Again
I am adding a refraction code and a background image to refract
// forked from Metrix's 2D Water Ripples - 2009-1-9
// Author: Richard Owen
//
// Based on some code i saw along time ago and I can not find now to reference
// Move your mouse around the stage and check out the water trail
// this works well as rain drops if you want to randomly drop points onto the surface
//

// Hi Richard Again
// I am adding a refraction code and a background image to refract
package 
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.utils.setInterval;

	
	/**
	 * ...
	 * @author ...
	 */
	public class Main extends Sprite 
	{
		
		private var Water:WaterMaterial;

		private var intervalID:uint = 0;
		
		private var dummyBMP:flash.display.BitmapData;
				

		public function Main():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void 
		{
			//Add backgroud pattern
			var size:int = 8;
			var gridwidth:int = 58;
			var gridheight:int = 58;
		    this.graphics.clear();
    		 	this.graphics.lineStyle(1, 0x000000, 1);
     		this.graphics.beginFill(0xFFFFFF, 1);
     		var col:int;
     		var row:int;
     		col=0;
     		row=0;
     		row = 0;
     		for (col = 0; col < gridwidth; col++)
     		{
     			for(row = 0; row < gridheight; row++){
		    			this.graphics.drawRect(col * size, row * size, size, size);
     			}
     		}
	        this.graphics.endFill();
			
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
		
			Water = new WaterMaterial();
			stage.addChild(Water);
	
			Water.addEventListener(MouseEvent.MOUSE_DOWN, MouseClicky)
			Water.addEventListener(MouseEvent.MOUSE_UP, MouseRelease)
			Water.addEventListener(MouseEvent.MOUSE_MOVE, MouseMove)

			intervalID = setInterval(WaterUpdate, 15);
			


		}
		

		
		private function MouseClicky(event:MouseEvent):void {
			Water.setDrop(event.localX, event.localY);
			Water.setDrop(event.localX-1, event.localY+1);
			Water.setDrop(event.localX+1, event.localY+1);
			Water.setDrop(event.localX-1, event.localY-1);
			Water.setDrop(event.localX+1, event.localY-1);
			
		};
		

		/*
		private function MouseClicky(event:MouseEvent):void {
			//top
			Water.setDrop(event.localX-2, event.localY-2);
			Water.setDrop(event.localX  , event.localY-2);
			Water.setDrop(event.localX+2, event.localY-2);

			//left
			Water.setDrop(event.localX-2, event.localY-2);
			Water.setDrop(event.localX-2, event.localY);
			Water.setDrop(event.localX-2, event.localY+2);
			
			//bottom
			Water.setDrop(event.localX-2, event.localY+2);
			Water.setDrop(event.localX  , event.localY+2);
			Water.setDrop(event.localX+2, event.localY+2);
			
			//right
			Water.setDrop(event.localX+2, event.localY-2);
			Water.setDrop(event.localX+2, event.localY);
			Water.setDrop(event.localX+2, event.localY+2);
			
		};
		*/
		
		private function MouseMove(event:MouseEvent):void {
			Water.setDrop(event.localX, event.localY);
		};

		
		public function WaterUpdate():void { 
			Water.forceRefresh();
		}
		
		private function MouseRelease(event:MouseEvent):void {
			//clearInterval(intervalID);
		};		
	}
	
}
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.InteractiveObject;
	import flash.geom.Rectangle;
	import flash.display.Sprite;
	import flash.events.Event;

	class WaterMaterial extends Sprite
	{
		private const MaxSize:uint = 460;
		private const TestScale:Number = 4.0;
		private const BufWidth:uint = Math.floor(MaxSize / TestScale);	
		private const BufHeight:uint = Math.floor(MaxSize / TestScale);
		private const MaxHeight:uint = 256;
		
		private const damping:Number = 7;
		
		private var Buffer1:Array = new Array(BufWidth);
		private var Buffer2:Array = new Array(BufWidth);
		private var tempBuff:Array;
		
		private var waterData:BitmapData;
		private var waterMap:Bitmap;
		private var dummyBMP:BitmapData;
		
		public function WaterMaterial() 
		{
			addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		public function init(e:Event): void {
			removeEventListener(Event.ADDED_TO_STAGE, init);
			
			//Create a bitmap to hold the rendering data			
			waterData = new BitmapData(BufWidth, BufHeight, true, 0x00000000);
			
			//add the water as a bitmap to the stage
			waterMap = new Bitmap(waterData);
			addChild(waterMap);
			
			//
			this.x = 0;
			this.y = 0;
			this.width = BufWidth * TestScale;
			this.height = BufHeight * TestScale;
			
			//Set all the water to a flat 0
			var col:int;
			var row:int;
			for (col = 0; col < BufWidth ; col++ ) {
  			    Buffer1[col] = new Array(BufHeight);
  			    Buffer2[col] = new Array(BufHeight);
				for (row = 1; row < BufHeight; row++) {
					Buffer1[col][row] = 0;
					Buffer2[col][row] = 0;
				}
			}	
			
			dummyBMP = new flash.display.BitmapData(500, 500);
			//dummyBMP.floodFill(10,10,0xff0000ff);
			dummyBMP.draw(root);
			//trace("0x"+dummyBMP.getPixel(150, 150).toString(16));
		}
		
		//Add a drop of water to the pool
		public function setDrop(dropX:uint, dropY:uint):void {
  			Buffer1[dropX][dropY] = MaxHeight;
		}
		
		//update the bitmap witht he current height
		private function HeightToPixel(col: int, row:int ): void {
			var pixelColor:uint;
			
			if(true){
				pixelColor = SolidColorWater(col, row);
			}
			else
			{
				pixelColor = RefractionColor(col, row);
			};
			
			waterData.setPixel32(col + 1, row + 1, pixelColor);
		}
		
		private function SolidColorWater(col: int, row: int): uint{
			var pixelColor:uint;
			
			pixelColor = Buffer2[col][row] << 24;
			pixelColor = pixelColor + 0xff;
		
			return pixelColor;
		}
		
		private function RefractionColor(col: int, row: int): uint {

			var rIndex:Number;
			rIndex = 2.0; //Water Refraction Index
			
			var y:int;
			var x:int;
			
			var xDiff:int;
			var xAngle:Number;
			var xRefraction:Number;
			var xDisplace:Number;
			
			var yDiff:int;
			var yAngle:Number;
			var yRefraction:Number;
			var yDisplace:Number;
			
			var newcolor:uint;
			

			x = col;
			y = row;
			//for y := 1 to MAXY-1 do begin
			//for(y = 1; y < BufHeight; y++){// to MAXY-1 do begin
				//for x := 1 to MAXX-1 do begin
				//for(x=1; x<BufWidth;x++){
					//xDiff := Trunc(WaveMap[x+1, y] - WaveMap[x, y]);

			if(((x > 1) && (x < BufWidth - 1)) && ((y > 1) && (y < BufHeight - 1)))
			{
				

					xDiff = Math.floor(Buffer2[x+1][y] - Buffer2[x][y]);
					//yDiff := Trunc(WaveMap[x, y+1] - WaveMap[x, y]);
					yDiff = Math.floor(Buffer2[x][y+1] - Buffer2[x][y]);
					
		
					//xAngle := arctan( xDiff );
					xAngle = Math.atan(xDiff);
					//xRefraction := arcsin( sin( xAngle ) / rIndex );
					xRefraction = Math.asin(Math.sin(xAngle)/rIndex);
					//xDisplace := Trunc( tan( xRefraction ) * xDiff );
					xDisplace = Math.floor(Math.tan(xRefraction) * xDiff);
					
					//yAngle := arctan( yDiff );
					yAngle = Math.atan(yDiff);
    					//yRefraction := arcsin( sin( yAngle ) / rIndex );
    					yRefraction = Math.asin( Math.sin( yAngle) / rIndex);
    					//yDisplace := Trunc( tan( yRefraction ) * yDiff );
    					yDisplace = Math.floor(Math.tan( yRefraction) * yDiff);
    					
    					//if xDiff < 0 then begin
    					if(xDiff < 0){
    						if(yDiff < 0 ){
    							//newcolor := BackgroundImage[x-xDisplace, y-yDisplace]
    							newcolor = dummyBMP.getPixel32(x-xDisplace, y-yDisplace);
    						}
    						else
    						{
    							//newcolor := BackgroundImage[x-xDisplace, y+yDisplace]
    							newcolor = dummyBMP.getPixel32(x-xDisplace, y+yDisplace);
    						}
    					}
    					else
    					{
    						if(yDiff < 0)
    						{
    							//newcolor := BackgroundImage[x+xDisplace, y-yDisplace]
    							newcolor = dummyBMP.getPixel32(x+xDisplace, y-yDisplace);
    						}
    						else
    						{
    							//newcolor := BackgroundImage[x+xDisplace, y+yDisplace]
    							newcolor = dummyBMP.getPixel32(x+xDisplace, y+yDisplace);
    						}
    					};
    					
    					//TargetImage[x, y] := newcolor;
    					//waterData.setPixel32(x, y, newcolor);
    				//}
			}
			else
			{
				newcolor = dummyBMP.getPixel32(x,y);
			};
			
			return newcolor;
			//return dummyBMP.getPixel32(10,10);
			
			//newcolor = Buffer2[col][row] << 24;
			//newcolor = newcolor + 0xff; 
			//return newcolor; 
		}
		
		public function dampEdgeOfWater(): void
		{
			var col:int;
			var row:int;
			
			//reduce the edge pixels
			for (col = 0; col < BufWidth; col++){
				Buffer2[col][0] = Math.floor(Buffer2[col][0] >> damping);
				Buffer2[col][BufHeight - 1] = Math.floor(Buffer2[col][BufHeight - 1] >> damping);
				
				if (Buffer2[col][0] < 0) Buffer2[col][0] = 0;
				if (Buffer2[col][BufHeight - 1] < 0) Buffer2[col][BufHeight - 1] = 0;
				
				//SetColor
				HeightToPixel(col, 0);
				HeightToPixel(col, BufHeight - 1);
			}
			
			for (row = 0; row < BufHeight; row++) {
				Buffer2[0][row] = Math.floor(Buffer2[0][row] >> damping);
				Buffer2[BufWidth - 1][row] = Math.floor(Buffer2[BufWidth - 1][row] >> damping);
				
				if (Buffer2[0][row] < 0) Buffer2[0][row] = 0;
				if (Buffer2[BufWidth - 1][row] < 0) Buffer2[BufWidth - 1][row] = 0;				

				//SetColor
				HeightToPixel(0, row);
				HeightToPixel(BufWidth - 1, row);
			}		
		}
				
		//smooth outthe ripples
		public function updateRipple():void {

			var col:int;
			var row:int;
			
			var oldVal:int;
			var newVal:int;
			var smoothedVal:uint;
			
			//Now process the inner rectangle of water
			for (col = 1; col < BufWidth - 1; col++ ) {
				for (row = 1; row < BufHeight - 1; row++) {
					
							smoothedVal = Buffer1[col - 1][row] + 
					                      Buffer1[col + 1][row] + 
										  Buffer1[col][row + 1] + 
										  Buffer1[col][row - 1] +
										  Buffer1[col - 1][row - 1] +
										  Buffer1[col - 1][row + 1] +
										  Buffer1[col + 1][row - 1] +
										  Buffer1[col + 1][row + 1];
										  
				
					newVal = Math.floor((((smoothedVal  ) >> 2 ) - Buffer2[col][row]));
										  
					newVal = newVal - ( newVal >> damping);
										  
					if (newVal < 0) newVal = 0;

					//small optimisation
					if(Buffer2[col][row]!= newVal) {
						Buffer2[col][row] = newVal;
						HeightToPixel(col, row);
					}
				}
			}
			
			dampEdgeOfWater()
		}
		
		//update the arrays
		public function forceRefresh(): void {
			
			//Update the water
			waterData.lock();
				updateRipple();
			waterData.unlock();
			
			
			//Swap buffer to retain direction and velocity
			tempBuff = Buffer2;
			Buffer2 = Buffer1;
			Buffer1 = tempBuff;
		}
	}