PDA

View Full Version : Array Problem/Instance/Copy?


zoimt
04-06-2008, 11:02 PM
Hello there. First of all, since im new here, I want to say hello to everybody.
Inspired by this (http://www.actionscript.org/resources/articles/717/1/Simple-3D-programming-for-AS20/Page1.html) tutorial I wanted to realize a 3D cube I can rotate, translate and scale.
But every time enter the onKeyPressed() function something's going really wrong. The values in the Array Cube.pnt are wrong. I added some trace outputs to testify my values and it seems that all x values in cube now are either 0 or 300, which appears to have somethingto do with the viewportTransform methode in Main.
The values should actually be like the originals + 10.
But I actually never touch the values of Cube.pnt, just when i call the trasX and trasZ function of the cube. I assume I have a problem with only instancing instead of really copying the arrays; but following many source the Array.slice function returns a deep copy of the array instead of just creating an instance refering to it.
Ok, here is the source, I hope it's readable, I put some test-trace calls in the code you can check @ the end of this post. Take a look @
transX loop: 310
transX loop: 10
transX loop: 10
transX loop: 310
transX loop: 10
transX loop: 310
transX loop: 310
transX loop: 10

they should be more like the original value (+50 or -50) + 10, therefore 60 or -40. I cannot explain that to me, spent the whole day with arguing about this. Pls have a look at this :confused:

best regards,
Zoimt.


package {
import flash.display.MovieClip;
import flash.display.*;
import flash.events.*;

public class Main extends MovieClip {

private var points:Array; // stores points of the scene
private var index:Array; // stores lineTo() index to draw better cubes
private var xSize, ySize:int; // scenesize
private var sp:Sprite; // place to draw the scene on
private var c:Cube; // cube object
private var g:Graphics;

public function Main() {
stage.frameRate=31;
points = new Array();
index = new Array();
sp = new Sprite();
g = sp.graphics;
xSize = ySize = 300;
trace("Start. At Main(). next: c = new Cube()"); // testing

c = new Cube(50); // creates a Cube with the scale of 50 to each side from center
points = c.getPoints(); // get points of the cube
trace("Back to Mainclass. Main.points[0] (x|y|z) = " + points[0].x + points[0].y + points[0].z);
index = c.getIndex();
points = perspectiveDivision(points);
points = viewportTransform(points);

addChild(sp);
drawScene();
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyPressed);
}
public function drawScene() {
g.lineStyle(1, 0x000000);
for (var i:int = 0; i < index.length; i++) {
g.moveTo(points[index[i][0]].x, points[index[i][0]].y);
for (var j = 1; j < 4; j++) {
g.lineTo(points[index[i][j]].x, points[index[i][j]].y);
}
g.lineTo(points[index[i][0]].x, points[index[i][0]].y);
}
}
private function onKeyPressed(evt:KeyboardEvent):void {
g.clear();
trace("Now @ Main.onKeyPressed. cube p0x " + c.getPoints()[0].x);
c.transX(10);
c.transZ(-10);
trace("Back to Main.onKeypressed");
points = new Array(); //clear old points
index = new Array(); //clear old indices
points = c.getPoints();
for (var i:int = 0; i < points.length; i++) {
trace("transX loop: " + points[i].x);
}
index = c.getIndex();
points = perspectiveDivision(points);
for (var i:int = 0; i < points.length; i++) {
trace("perspDevided x: " + points[i].x);
}
points = viewportTransform(points)
drawScene();
}
public function perspectiveDivision(points:Array) {
// goes through the array and divides its x and y values by its z values
var tmp:Array = points.slice(0, points.length);
for (var i:int; i < points.length; i++) {
trace("tmpX b4 perspDev " + tmp[i].x + " and Z equals: " + points[i].z);
tmp[i].x = tmp[i].x / tmp[i].z;
tmp[i].y = tmp[i].y / tmp[i].z;
trace("tmpX after perspDev " + tmp[i].x);
}
return tmp.slice(0, tmp.length);
}
public function viewportTransform(points:Array) {
// multiplies resuslting values
var tmp:Array = points.slice(0, points.length);
for (var i:int; i< points.length; i++) {
tmp[i].x = 0.5 * (tmp[i].x + 1) * xSize;
tmp[i].y = 0.5 * (tmp[i].y + 1) * ySize;
trace("Viewportpunkt " + i + " x: " + tmp[i].x);
trace("Viewportpunkt " + i + " y: " + tmp[i].y);
}
return tmp.slice(0, tmp.length);
}
public function arrayCopy(a:Array) {
return a.slice(0,a.length);
}
}
}


And here's the Cube:

package{
public class Cube {
private var pts, ref, index:Array;
private var scal:int;

public function Cube(scal:int) {
this.scal = scal;
trace("Constructor of Cube. scale value is:" + this.scal);
pts = new Array();
index = new Array();
trace("generateCube(scal); just a reminder: scal = " + scal);
generateCube(scal);
this.ref = pts.slice(0,pts.length);

}
public function getScale() {
return scal;
}
public function setScale(scal:int) {
this.scal = scal;
generateCube(scal);
}
public function getPoints() {
trace("p0x @ getPoints: " + pts[0].x);
return pts.slice(0, pts.length);
}
public function setPoints(pts:Array):void {
this.pts = pts;
}
public function getIndex() {
return index.slice(0, index.length);
}
public function generateCube(scal:int):void {
// creates pointmatrix
trace("now @ generateCube(). The scalevalue still is:" + scal);
pts.push({x:-scal, y:-scal, z:-scal});
trace("Cube.pts[0] (x|y|z): " + pts[0].x + pts[0].y + pts[0].z);
pts.push({x:scal, y:-scal, z:-scal});
pts.push({x:scal, y:scal, z:-scal});
pts.push({x:-scal, y:scal, z:-scal});
pts.push({x:-scal, y:-scal, z:scal});
pts.push({x:scal, y:-scal, z:scal});
pts.push({x:scal, y:scal, z:scal});
pts.push({x:-scal, y:scal, z:scal});

// indicates line-draw order
index.push([0, 1, 2, 3]);
index.push([4, 5, 6, 7]);
index.push([0, 4, 7, 3]);
index.push([0, 1, 5, 4]);
index.push([1, 5, 6, 2]);
index.push([3, 7, 6, 2]);
}
public function transX(transX:int):void {
trace("now @ Cube.transX. transX-factor= " + transX);
for (var i:int = 0; i < pts.length; i++) {
pts[i].x += transX;
trace("transX loop: " + pts[i].x);
}
}
public function trasnY(transY:int):void {
for (var i:int = 0; i < pts.length; i++) {
pts[i].y += transY;
}
}
public function transZ(transZ:int):void {
for (var i:int = 0; i < pts.length; i++) {
pts[i].z += transZ;
}
}
}
}


And this is the Output:
Start. At Main(). next: c = new Cube()
Constructor of Cube. scale value is:50
generateCube(scal); just a reminder: scal = 50
now @ generateCube(). The scalevalue still is:50
Cube.pts[0] (x|y|z): -50-50-50
p0x @ getPoints: -50
Back to Mainclass. Main.points[0] (x|y|z) = -50-50-50
tmpX b4 perspDev -50 and Z equals: -50
tmpX after perspDev 1
tmpX b4 perspDev 50 and Z equals: -50
tmpX after perspDev -1
tmpX b4 perspDev 50 and Z equals: -50
tmpX after perspDev -1
tmpX b4 perspDev -50 and Z equals: -50
tmpX after perspDev 1
tmpX b4 perspDev -50 and Z equals: 50
tmpX after perspDev -1
tmpX b4 perspDev 50 and Z equals: 50
tmpX after perspDev 1
tmpX b4 perspDev 50 and Z equals: 50
tmpX after perspDev 1
tmpX b4 perspDev -50 and Z equals: 50
tmpX after perspDev -1
Viewportpunkt 0 x: 300
Viewportpunkt 0 y: 300
Viewportpunkt 1 x: 0
Viewportpunkt 1 y: 300
Viewportpunkt 2 x: 0
Viewportpunkt 2 y: 0
Viewportpunkt 3 x: 300
Viewportpunkt 3 y: 0
Viewportpunkt 4 x: 0
Viewportpunkt 4 y: 0
Viewportpunkt 5 x: 300
Viewportpunkt 5 y: 0
Viewportpunkt 6 x: 300
Viewportpunkt 6 y: 300
Viewportpunkt 7 x: 0
Viewportpunkt 7 y: 300
p0x @ getPoints: 300
Now @ Main.onKeyPressed. cube p0x 300
now @ Cube.transX. transX-factor= 10
transX loop: 310
transX loop: 10
transX loop: 10
transX loop: 310
transX loop: 10
transX loop: 310
transX loop: 310
transX loop: 10
Back to Main.onKeypressed
p0x @ getPoints: 310
transX loop: 310
transX loop: 10
transX loop: 10
transX loop: 310
transX loop: 10
transX loop: 310
transX loop: 310
transX loop: 10
tmpX b4 perspDev 310 and Z equals: -60
tmpX after perspDev -5.166666666666667
tmpX b4 perspDev 10 and Z equals: -60
tmpX after perspDev -0.16666666666666666
tmpX b4 perspDev 10 and Z equals: -60
tmpX after perspDev -0.16666666666666666
tmpX b4 perspDev 310 and Z equals: -60
tmpX after perspDev -5.166666666666667
tmpX b4 perspDev 10 and Z equals: 40
tmpX after perspDev 0.25
tmpX b4 perspDev 310 and Z equals: 40
tmpX after perspDev 7.75
tmpX b4 perspDev 310 and Z equals: 40
tmpX after perspDev 7.75
tmpX b4 perspDev 10 and Z equals: 40
tmpX after perspDev 0.25
perspDevided x: -5.166666666666667
perspDevided x: -0.16666666666666666
perspDevided x: -0.16666666666666666
perspDevided x: -5.166666666666667
perspDevided x: 0.25
perspDevided x: 7.75
perspDevided x: 7.75
perspDevided x: 0.25
Viewportpunkt 0 x: -625
Viewportpunkt 0 y: -600
Viewportpunkt 1 x: 125
Viewportpunkt 1 y: -600
Viewportpunkt 2 x: 125
Viewportpunkt 2 y: 150
Viewportpunkt 3 x: -625
Viewportpunkt 3 y: 150
Viewportpunkt 4 x: 187.5
Viewportpunkt 4 y: 150
Viewportpunkt 5 x: 1312.5
Viewportpunkt 5 y: 150
Viewportpunkt 6 x: 1312.5
Viewportpunkt 6 y: 1275
Viewportpunkt 7 x: 187.5
Viewportpunkt 7 y: 1275

Darcey
04-07-2008, 02:02 AM
Hi,

Well not quite sure as to what is going on there but I could suggest an eaiser approach.

I have cooked up 3 cubes.

1. Wire frame textured
1. Flat shade textured
1. Bitmap textured

and all rotated via onEnterFrame

http://www.allforthecode.co.uk/forum/viewtopic.php?f=10&t=164


ps. i forgot to set the bitmap jpg path correctly so the bitmap textured cube is all black but I think you'll get the idea.

zoimt
04-07-2008, 10:11 AM
Thank you. I am very new to Flash and Actionscript so if I am wrong, correct me but as I understood it, Papervision is an external 3D engine. I dont want to use an external engine, my approach is working fine as long as I dont call the onKeypress() function. The problem is also not the math behind it, its just that the values of Points.pnt get screwed. The funny thing is, I dont touch the values of cube(which should be somewhere between 50 and -50 when created [works! cube's displayed correct]). But when i want to add 10 to these values after pressing a key, the values are somewhere between 0 and 300 (or 0 and 500 if i change xSize to 500, the viewport size)
Maybe my explaination was a little vague so I am going through the source and explain where the problem appears:

Ok, here we go and start in Main. The constructor of Main does the following:
1.) generate two Arrays, one is storing the Points of any object and one is to support the drawline-function with the order in which it moves from lineTo() line. Then i create an instance of cube with the size of 50 to each of the 6 sides (x, y, z, -x, -y, -z). Furthermore, I create a sprite to draw the lines of the cube on. I dont mind about shading etc at the moment, I just want to draw wire lines from point A to B to C to A.

public function Main() {

points = new Array();
index = new Array();
c = new Cube(50);

}
2.) I create an object of the type Cube. Cube itself has 2 arrays again, one for the points and one for the order in which the points are connected with the lineTo() function. The constructer of Cube creates those two arrays. Then it calls the generateCube(scal) function so the two arrays get filled with information (the 8 points of a cube, each point has a x, y and z coordinate and the order in which the lines from point to get rendered)


public function Cube(scal:int) {

pts = new Array();
index = new Array();
generateCube(scal);
}

public function generateCube(scal:int):void {
// creates pointmatrix

pts.push({x:-scal, y:-scal, z:-scal});
pts.push({x:scal, y:-scal, z:-scal});
pts.push({x:scal, y:scal, z:-scal});
pts.push({x:-scal, y:scal, z:-scal});
pts.push({x:-scal, y:-scal, z:scal});
pts.push({x:scal, y:-scal, z:scal});
pts.push({x:scal, y:scal, z:scal});
pts.push({x:-scal, y:scal, z:scal});

// indicates line-draw order
index.push([0, 1, 2, 3]);
index.push([4, 5, 6, 7]);
index.push([0, 4, 7, 3]);
index.push([0, 1, 5, 4]);
index.push([1, 5, 6, 2]);
index.push([3, 7, 6, 2]);
}


3.) Ok, after creating the cube, I need copy the points and index information from the cube instance to my Main. This is also done in the constructor of Main, just a few lines further.
public function Main() {

...
points = c.getPoints();
index = c.getIndex();
...
points = perspectiveDivision(points);
points = viewportTransform(points)
}

4.) Now the whole thing starts. To transform a 3D model into 2D space, I need to apply perspective division first. This is just done by cycling through the pointsArray in Main, each point[i].x and point[i].y is divided by its z-value.
The perspective division, no matter what comes, must be a Value between -1 and 1. Its pretty hard to see anything on the screen with these values. This is why the viewportTransform() function is applied. So, I first add +1 to those values to ensure I have only positive values and then multiply this by 0.5 to get values between 0 and 1 instead of 0 - 2. The resulting values are then multiplied by the viewport size (xSize).
If my viewport was xSize != ySize the whole thing wouldnt work, more calculations must be involved to solve this. But for my case (squaresized viewport, no camera animation) this just works fine.


public function perspectiveDivision(points:Array) {
// goes through the array and divides its x and y values by its z values
var tmp:Array = points.slice(0, points.length);
for (var i:int; i < points.length; i++) {
tmp[i].x = tmp[i].x / tmp[i].z;
tmp[i].y = tmp[i].y / tmp[i].z;
}
return tmp.slice(0, tmp.length);
}

public function viewportTransform(points:Array) {
// multiplies resuslting values
var tmp:Array = points.slice(0, points.length);
for (var i:int; i< points.length; i++) {
tmp[i].x = 0.5 * (tmp[i].x + 1) * xSize;
tmp[i].y = 0.5 * (tmp[i].y + 1) * ySize;
}
return tmp.slice(0, tmp.length);
}

5.) After that I call the drawScene() function, go through each points x and y values and draw lines from point to point. The order I move the lineTo() is defined in the indexArray (as I mentioned before, I know^^)

public function drawScene() {
g.lineStyle(1, 0x000000);
for (var i:int = 0; i < index.length; i++) {
g.moveTo(points[index[i][0]].x, points[index[i][0]].y);
for (var j = 1; j < 4; j++) {
g.lineTo(points[index[i][j]].x, points[index[i][j]].y);
}
g.lineTo(points[index[i][0]].x, points[index[i][0]].y);
}
}

This whole thing just works, the cube is drawn correctly. Ok, what's going wrong now is everything that follows...

I expect a key press. When that happens, the onKeypress function is called, doing the following:
First it clears the by now drawn cube on the sprite. Then i call the function transX and transZ of the cube and give them 10, respectively -10 for the z axis so it moves away from the origin.
In the cube class, we have those two functions transX and transZ, working as shown below:

public function transZ(transZ:int):void {
for (var i:int = 0; i < pts.length; i++) {
pts[i].z += transZ;
}
}

same goes for transX, just with the difference that pts[i].x is called instead of pts[i].z
However. Now the error appears:
The pts-Array in Cube doesnt contain values of -50 to 50 anymore, instead values between 0 and Xsize or, if you want to say so, it contains the values of Main.pts after perspectiveDivision() and viewportTransform.
And this, only this is my problem.
See, I dont need papervision, I just need to find the reason why in the hell now all values in Cube.pts (the array that stores the coordinates of the cube object, in object space if you want to say so) are screwed.
And I cant explain to me why!
I hope this explaination helps you to help me.
Best regards,
Zoimt.

zoimt
04-07-2008, 01:59 PM
Oh c'mon, its not that difficult to understand my problem if you just read it once.
Please check the source i posted, its just an problem based on copying an array, respectly copying the content of the array instead of creating instances pointing to a reference. At least I think so.
Array.slice(0, Array.length) returns allegedly a new array. But this new array seems to contain only instances refering to the same objects inside, again. Is there any possibility to create deep copies instead of just instances(besides a for-loop to cycle through the array)?

wvxvw
04-07-2008, 03:09 PM
Sorry, I don't know how to fix your problem, but I'm sure that slice doesn't duplicate array's content (here's an example):
var o:Object = {a:1};
var arr:Array = [o];
var arr0:Array = arr.slice();
arr0[0].a = 2;
trace(arr0[0].a);
trace(arr[0].a);
trace(o.a);
As a general advice in such case I wouln't use arrays. I'd rather created an object with fixed number of properties like .point0... .point16 and any time I need another set of points - had this object created with new operator, so I'd be sure it doesn't point to anything (has it's own unique values).

eNine
04-27-2008, 08:19 PM
i'm struggling with a similar issue....i really wish there was a convenient way to create a deep copy of an array...

wvxvw
04-27-2008, 09:01 PM
var arr:Array = [{a:0},{a:1},{a:2},{a:3},{a:4},{a:5},{a:6},{a:7},{a :8},{a:9},{a:10}];
// we'll need this for the test
for each(var o:Object in arr){
o.toString = function():String {return 'a='+this.a};
}
trace(arr);
var createDuplicate:Function = function(a:*,index:int,ar:Array):void {
var obj:* = {};
var s:String;
for(s in a){
if(a[s] is Function){
obj[s] = function(){return a[s]()};
} else {
obj[s]=s;
}
}
this.rarr.push(obj);
}
function duplicateArray(ar:Array):Array {
//toString here is also for the test purposes
var foo:Object = {rarr:[],toString:function():String{return '[foo]'}};
ar.map(createDuplicate, foo);
return foo.rarr;
}
var newArr:Array = duplicateArray(arr);
trace(newArr);
arr[5].a = 16;
newArr[5] = 'abcd';
trace(arr+'\n'+newArr)
This may be a quick solution, but, better solution would be not using arrays for this specific purpose. Have your objects in array have some .clone() or duplicate() method (like XML for e.g.) and call this method on all the object on array you want to duplicate - this would be proper.
And, just keep in mind, this will duplicate only properties, not functions (and if the first array has another not primitive properties, they won't be duplicated.