[F8] What's wrong with my rectangle?
First off, it's embarrassing to even post this considering how simple it is to code, but it just isn't working and I don't know why.
The goal: uniformly change the scale of a rectangle relative to its center point.
The problem: application of scalar value to rectangle dimensions creates unexpected dimensions. You'll see what I'm talking about if you have the test window maximized at 100% magnification.
Note: The problem has persisted even after converting everything to Point and Matrix classes. Thanks in advance.
Class code:
Code:
// Inside ViewPort.as ...
// import the classes we need to build this custom object
import flash.geom.Point
import flash.geom.Rectangle
import flash.geom.Matrix
/* Class: ViewPort
* Version: 1.0.1
* Desc: A custom class designed to view a nested movieclip
* through a positionable and scalable scrollRect.
*
* NOTES:
* - This is intended to serve as the clipping region of
* a game engine.
* - The MovieClip "backBuffer_mc" is a container that holds
* a level map, while "frontBuffer_mc" is assigned the
* Rectangle "view."
* - "backBuffer_mc" is nested within "frontBuffer_mc".
* - The position of "view" is translated, then "view" is
* re-applied to "frontBuffer_mc" to achieve camera tracking.
* - Zoom effects are achieved by scaling "view" before
* translation, then increasing the _xscale and _yscale
* of "frontBuffer_mc" to preserve the original dimensions
* of "view".
*/
class ViewPort {
/* Class Properties */
private static var frontBufferDepth:Number = 1; // place the clipping layer above the level map layer
private static var backBufferDepth:Number = 0; // place the level map layer below the clipping layer
/* Instance Properties */
private var scaleMatrix:Matrix; // 1. scale is always applied first
private var translateMatrix:Matrix; // 2. translation is always applied second
private var viewSize:Object; // initial width and height of rectangle
private var viewTL_In:Point; // default position of top-left corner of rectangle
private var viewBR_In:Point; // default position of bottom-right corner of rectangle
private var viewTL_Out:Point; // scaled, translated top-left corner of rectangle
private var viewBR_Out:Point; // scaled, translated bottom-right corner of rectangle
private var viewCenter:Point; // centerpoint of the rectangle
private var viewScale:Number = 1; // scale of the rectangle
private var viewInverse:Number = 1; // _xscale, _yscale used to preserve rectangle
private var viewZoomStep:Number = 0; // value ranging from 0 to (2 * Math.PI)
private var view:Rectangle; // the clipping rectangle
private var target_mc:MovieClip; // the layer that is loading this instance
private var frontBuffer_mc:MovieClip; // the clipping layer
private var backBuffer_mc:MovieClip; // the level map layer
private var targetDepth:Number; // depth target_mc is loading our instance into
public function ViewPort(t:MovieClip, d:Number, s:String, x:Number, y:Number, w:Number, h:Number) {
/* Constructor */
// a nested argument checking function will
// be inserted here and will conditionally init
// the class, otherwise abort.
initProperties(t, d, s, x, y, w, h);
}
private function initProperties(t:MovieClip, d:Number, s:String, x:Number, y:Number, w:Number, h:Number):Void {
moveView(x, y); // center our view
setViewSize(w, h); // size the view (at 100% zoom)
defineViewRect(); // create the rectangle of our view
setTargetDepth(d); // store the depth our instance is loading into
buildViewPort(t); // create our instance on the layer that is loading it
clipRender(); // apply scrollRect to frontBuffer_mc
loadSurface(s); // load a graphic to test the behavior of the class.
}
private function setTargetDepth(d:Number):Void {
targetDepth = d;
}
private function buildViewPort(t:MovieClip, d:Number):Void {
buildFrontBuffer(t);
buildBackBuffer();
}
private function buildFrontBuffer(t:MovieClip):Void {
frontBuffer_mc = t.createEmptyMovieClip("frontBuffer_mc", frontBufferDepth);
}
private function buildBackBuffer():Void {
backBuffer_mc = frontBuffer_mc.createEmptyMovieClip("backBuffer_mc", backBufferDepth);
}
private function moveView(x:Number, y:Number):Void {
viewCenter = new Point(x, y);
}
private function setViewSize(newW:Number, newH:Number):Void {
var newSize:Object = {width : newW, height : newH};
viewSize = newSize;
/* Important */ // <= = = = = = = = = = = = = = = = = = = = = = =
// "viewCenter" needs to rest at the center of "view",
// so place top-left corner of "view" at a negative value
// relative to "viewCenter" and place bottom-right corner of
// "view" at a positive value relative to "viewCenter"
viewTL_In = new Point(-(viewSize.width / 2), -(viewSize.height / 2));
viewBR_In = new Point((viewSize.width / 2), (viewSize.height / 2));
}
private function buildMatrices():Void {
scaleMatrix = new Matrix(1, 0, 0, 1, 0, 0);
translateMatrix = new Matrix(1, 0, 0, 1, 1, 1);
}
private function defineViewRect():Void {
/* Error ? */ // < = = = = = = = = = = = = = = = = = = = = = = = = =
// Is this where the error is?
buildMatrices();
scaleMatrix.scale(viewScale, viewScale);
translateMatrix.translate(viewCenter.x, viewCenter.y);
scaleMatrix.concat(translateMatrix);
viewTL_Out = scaleMatrix.transformPoint(viewTL_In);
viewBR_Out = scaleMatrix.transformPoint(viewBR_In);
}
private function buildView():Void {
view = new Rectangle(viewTL_Out.x,
viewTL_Out.y,
viewBR_Out.x,
viewBR_Out.y);
}
private function refreshView():Void {
clipRender();
}
private function loadSurface(i:String):Void {
backBuffer_mc.loadMovie(i);
}
private function clipRender():Void {
frontBuffer_mc.scrollRect = view;
}
private function preserveFullSize():Void {
viewInverse = (1 / viewScale) * 100;
frontBuffer_mc._xscale = frontBuffer_mc._yscale = viewInverse;
}
public function zoomView():Void {
/* Error ? */ // < = = = = = = = = = = = = = = = = = = = = = = = =
// Is this where the error is?
var zoomOrigin:Number = 0.75;
var zoomAmplitude:Number = 0.25;
var zoomX:Number = 320 + (Math.cos(viewZoomStep * 4) * 30);
var zoomY:Number = 240 + (Math.sin(viewZoomStep * 4) * 30);
viewScale = zoomOrigin + (Math.cos(viewZoomStep) * zoomAmplitude);
moveView(zoomX, zoomY);
defineViewRect();
buildView();
refreshView();
preserveFullSize();
if(viewZoomStep == (Math.PI * 2)) {
viewZoomStep = 0;
} else {
viewZoomStep += (Math.PI / 180);
}
//debugVars();
}
private function debugVars():Void {
/* Debugger */
// use this to spit out vars
for(var p:String in this) {
if(p.indexOf("view", 0) != (-1) || p.indexOf("Matrix") != (-1)) {
trace(p + " = " + this[p]);
}
}
}
}
Flash document code:
Code:
// Set your document to 640 x 480, background #639A9C
// in Layer 1, Frame 1 of the main timeline...
var myImage:String; // supply the path of a local image of your choosing
var testRender:ViewPort = new ViewPort(this, 0, myImage, 320, 240, 640, 480);
this.onEnterFrame = function () {
testRender.zoomView();
};