{AS3} Solved: Distorting the bottom half of the Stage

Update: I’ve seen where the SWF encounters some invalid bitmapData. Run the code locally in your own FLA and see what happens. I’ve since updated my code quite a lot to perform some extra cool things, but the supplied code here has not been updated with it. Should still work though.

Let me first wish you all an enjoyable weekend! With that said, let’s begin.

The other day I was reflecting on ways to properly distort the bottom half (for instance) of an application’s Stage, with elements animating, etc. and how to best achieve the effect. After spending some time evaluating the problem and spending time barking up the wrong trees (yes, I also looked and tried a Matrix solution which can’t support what I was after since it’s 3×3), I thought about beginBitmapFill and how it might be used to pull such an effect off with desirable results. I spent a lot of time using Google looking for something I might use to make things work.

 

Example screenshot 

Demonstration link (new page)

Using a the combination of capturing the entire Stage, copying pixels from that into another bitmapData object, and then manipulating (the bit I was really after) that with a drawPlane method (I wish I could give props to the author of that particular code, I’ve been downloading oodles of stuff and I can’t keep them straight after a while). I am positive the genius comes from┬áZeh Fernando.

Here is the drawPlane method:

package {
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.geom.Point;

	public function drawPlane(graphics:Graphics, bitmap:BitmapData, p1:Point, p2:Point, p3:Point, p4:Point) : void {
		var pc:Point = getIntersection(p1, p4, p2, p3); // Central point

		// If no intersection between two diagonals, doesn't draw anything
		if (!Boolean(pc)) return;

		// Lenghts of first diagonal
		var ll1:Number = Point.distance(p1, pc);
		var ll2:Number = Point.distance(pc, p4);

		// Lengths of second diagonal
		var lr1:Number = Point.distance(p2, pc);
		var lr2:Number = Point.distance(pc, p3);

		// Ratio between diagonals
		var f:Number = (ll1 + ll2) / (lr1 + lr2);

		// Draws the triangle
		graphics.clear();
		graphics.beginBitmapFill(bitmap, null, false, true);

		graphics.drawTriangles(
			Vector.<Number>([p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y]),
			Vector.<int>([0,1,2, 1,3,2]),
			Vector.<Number>([0,0,(1/ll2)*f, 1,0,(1/lr2), 0,1,(1/lr1), 1,1,(1/ll1)*f]) // Magic
		);
	}
}

import flash.geom.Point;
function getIntersection(p1:Point, p2:Point, p3:Point, p4:Point): Point {
	// Returns a point containing the intersection between two lines
	// http://keith-hair.net/blog/2008/08/04/find-intersection-point-of-two-lines-in-as3/
	// http://www.gamedev.pastebin.com/f49a054c1

	var a1:Number = p2.y - p1.y;
	var b1:Number = p1.x - p2.x;
	var a2:Number = p4.y - p3.y;
	var b2:Number = p3.x - p4.x;

	var denom:Number = a1 * b2 - a2 * b1;
	if (denom == 0) return null;

	var c1:Number = p2.x * p1.y - p1.x * p2.y;
	var c2:Number = p4.x * p3.y - p3.x * p4.y;

	var p:Point = new Point((b1 * c2 - b2 * c1)/denom, (a2 * c1 - a1 * c2)/denom);

	if (Point.distance(p, p2) > Point.distance(p1, p2)) return null;
	if (Point.distance(p, p1) > Point.distance(p1, p2)) return null;
	if (Point.distance(p, p4) > Point.distance(p3, p4)) return null;
	if (Point.distance(p, p3) > Point.distance(p3, p4)) return null;

	return p;
}

And here is how it was implemented (I am sure it could be streamlined). If you have suggestions, please send them along. This is a document Class.

package  {
	import flash.display.*;
    import flash.geom.*;
    import flash.events.*;
	import drawPlane;
	import com.greensock.TweenMax;

	public class MyDocumentClass extends MovieClip {

		private var sprite:Sprite = new Sprite();

		public function AngledSurface_001B()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			var bitmapdata:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight,true, 0xFF333333);
			bitmapdata.draw(stage);
			var bitmapDataA: BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight/2, true);
			bitmapDataA.copyPixels(bitmapdata, new Rectangle(0, stage.stageHeight/2, stage.stageWidth, stage.stageHeight/2), new Point(0, 0), null, null,false);
			sprite.y = 225;
			addChild(sprite);
			stage.addEventListener(Event.ENTER_FRAME, manipulateBottomStage);
			TweenMax.to(words,4,{y:380, yoyo:true, repeat:-1});
		}

		private function manipulateBottomStage(event:Event):void
		{
			var bitmapdata:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight,true, 0xFF333333);
			sprite.visible = false;
			bitmapdata.draw(stage, null, null, null, null, true);
			var bitmapDataA: BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight/2, true);
			bitmapDataA.copyPixels(bitmapdata, new Rectangle(0, stage.stageHeight/2, stage.stageWidth, stage.stageHeight/2), new Point(0, 0), null, null, true);
			var p0:Point = new Point(0,0);
			var p1:Point = new Point(845,0);
			var p2:Point = new Point(-100,225);
			var p3:Point = new Point(945,225);
			drawPlane(sprite.graphics,bitmapDataA,p0,p1,p2,p3);
			sprite.visible = true;
		}
	}
}

If you find this interesting, enjoy. A post like this would have saved me at least a day’s worth of searching.

One thing to note, the Stage is 845 x 450, so the control points are beyond the boundaries of the visual Stage. If you were to move p2 and p3 within the Stage boundaries far enough, you’ll be able to see things below the sprite showing through, since the sprite’s contents are being manipulated and you’ll end up with empty “wedges” on the left and the right. You could always add a little bit to the method to draws over those areas with the Stage color to prevent that from happening.

Related Posts Plugin for WordPress, Blogger...

One thought on “{AS3} Solved: Distorting the bottom half of the Stage

Leave a Reply

Your email address will not be published. Required fields are marked *


1 + seven =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>