Flash CS4 Polar Cube (one-to-one mapping)

Intro

Mapping a sphere to a cube becomes a fundamental issue in CS4, where vertex to UV data is a one-to-one map. Typical UV data brought in from Blender or 3DSMax, which have multiple uv data points per vertex (or triangle fan), will not work with the drawTriangles method of CS4. There are work-a-rounds, but eventually it all boils down to creating some type of one-to-one mapping scheme.

An illustrative step in this process is the creation of a polar cube – a polar cube created completely from the polar parametric equations of an octagonal cubic (not pieced together with six planes (or 8 planes in this case) as is done in Papervision3D and other such software). Got a little ahead of myself here – the techniques described can be used to make any number of sides. I kept referring to a cube but created an octagonal as was pointed out by makc3d in the comments. The equations can be used to created a sculpty prim maker which will be posted soon.

Not 8 separate sides, all one structure.

Polar Cube

Polar Cube

Demo: Click Here

Source: Click Here

Polar Cube Derivation

The parametric mapping equations (for a polar cube) are simple, and for a 2D boil down to

x = r(θ)*cos(θ)
y = r(θ)*sin(θ)

and in 3D,

x = r(θ, Φ)*cos(θ)sin(Φ)
y = r(θ, Φ)*sin(θ)sin(Φ)
z = r(θ, Φ)*cos(Φ)

where r is function of angle (not constant as in the spherical case).

Solving for the 2D case, r(θ) is given by

θ: 0 to 90, r(θ) = 1/(sin(θ) + cos(θ))
θ: 90 to 180, r(θ) = 1/(sin(θ) – cos(θ))
θ: 180 to 270, r(θ) = -1/(sin(θ) + cos(θ))
θ: 270 to360, r(θ) = -1/(sin(θ) – cos(θ))

These solutions are easily obtained by substitution x = r(θ)*cos(θ), and y = r(θ)*sin(θ) into the linear equations shown in the figure below and solving for r(θ).

2D Polar Case

2D Polar Case

The generalized solution is

r(a, b, θ) = a/(sin(θ) + b*cos(θ))

where

θ: 0 to 90 (a=1, b=1), θ: 90 to 180 (a=1, b=-1), θ: 180 to 270 (a=-1, b=-1), θ: 270 to360 (a=-1, b=-1) Of interest, the values of the a, b parameters correspond to all possible combinations of 1 and -1, similar two coins which gives (HH, HT, TH, TT) where H=1, and T=-1.

3D Case

Following the approach above, for the 3D case (where 8 possible planes corresponding to the 8 sides of the polar cube), the general solution is given by

r(a, b, c, θ, Φ) = a/(cos(Φ)+b*sin(θ)sin(Φ)+c*cos(θ)sin(Φ))

where the parameters a, b, c correspond to flipping three coins at the same time with 8 possible outcomes (HHH, HHT, HTH, HTT, THH, THT, TTH, TTT or 111, 11-1, 1-11, 1-1-1, -111,- 11-1, -1-11, -1-1-1), corresponding to the 8 sides of our polar cube.

The eight possibilities used to map the vertices of the cube are given below:

θ: 0 to 90, Φ:0 to 90 (-1,-1,-1)
θ: 90 to 180, Φ:0 to 90 (-1,-1,1)
θ: 180 to 270, Φ:0 to 90 (-1,1,1)
θ: 270 to360, Φ:0 to 90 (-1,1,-1)

θ: 0 to 90, Φ:90 to 180 (1,1,1)
θ: 90 to 180, Φ:90 to 180 (1,1,-1)
θ: 180 to 270, Φ:90 to 180 (1,-1,-1)
θ: 270 to360, Φ:90 to 180 (1,-1,1)

Coding the Parametric Equations

The coding is straightforward. The cube’s vertices are derived from the parametric form given above and its indices are derived from a one-to-one mapping of a sphere onto a cube. That’s why the spherically mapped image, shown in the image above, lays on the cube without distortion.

for (var i:int = 0 ; i!=rows; i++) {
ix= i/(rows-1)*Math.PI*2.0;

for (var j:int = 0 ; j!=cols; j++) {
iy= (j/(cols-1)-0.5)*Math.PI;

// 8 planes 8case

if(ix>=0 && ix<2*Math.PI/4){

//θ: 0-90, Φ: 0-90, (-1,-1,-1)

if(iy>=-2*Math.PI/4 && iy<0){
varMyrIs=rFunc(-1,-1,-1,ix,iy);
}else{

//θ: 0-90, Φ: 90-180, (1,1,1)

varMyrIs=rFunc(1,1,1,ix,iy);}
}

if(ix>=2*Math.PI/4 && ix<2*Math.PI/2){

//θ: 90-180, Φ: 0-90, (-1,-1,1)

if(iy>=-2*Math.PI/4 && iy<0){
varMyrIs=rFunc(-1,-1,1,ix,iy);
}else{

//θ: 90-180, Φ: 90-180, (1,1,-1)

varMyrIs=rFunc(1,1,-1,ix,iy);}
}

if(ix>=2*Math.PI/2 && ix<6*Math.PI/4){

//θ: 180-270, Φ: 0-90, (1,-1,1)

if(iy>=-2*Math.PI/4 && iy<0){
varMyrIs=rFunc(-1,1,1,ix,iy);
}else{

//θ: 180-270, Φ: 90-180, (-1,-1,1)

varMyrIs=rFunc(1,-1,-1,ix,iy);}
}

if(ix>=6*Math.PI/4 && ix<=2*Math.PI){

//θ: 270-360, Φ: 0-90, (-1,1,-1)

if(iy>=-2*Math.PI/4 && iy<0){

varMyrIs=rFunc(-1,1,-1,ix,iy);
}else{

//θ: 270-360, Φ: 90-180, (1,-1,1)

varMyrIs=rFunc(1,-1,1,ix,iy);}
}

//Polar Cube
paraVec = new Vector3D(
varMyrIs*Math.cos(iy)*Math.cos(ix), varMyrIs*Math.sin(iy), varMyrIs*Math.cos(iy)*Math.sin(ix));

//Collect vetices in the verts Vector array
verts.push(radx*paraVec.x,rady*paraVec.y,radz*paraVec.z);

//Load uvt data
uvtData.push( i/(rows-1),j/(cols-1), 0.0);
//Initialize projected vertices
projectedVerts.push(0.0,0.0);
}
}

Now you’ll ready to start Knowledge Space. To see the entire code click the more link below:

Polar Cube

package {

import flash.display.*;
import flash.geom.*;
import flash.events.Event;
import flash.utils.Timer;
import flash.events.TimerEvent;

public class PolarCube extends Sprite {
//Embed your texture
[Embed(source=”anthroLabWeb.jpg”)]
private var YourImage:Class;
//Primitive Declarations
private var primitive:Sprite;
private var myTimer:Timer;
private var myCount:int=0;
private var paraVec:Vector3D;
private var bitmapData:BitmapData;
private var rows:int=33;
private var cols:int=33;
private var ix:Number;
private var iy:Number;
//Elliptical Parameters
private var radx:Number=1;
private var rady:Number=1;
private var radz:Number=1;
//Vertice Data
private var verts:Vector.<Number>;
private var indices:Vector.<int>;
private var uvtData:Vector.<Number>;
private var uvtShadingData:Vector.<Number>;
private var projectedVerts:Vector.<Number>;

private var perspective:PerspectiveProjection;
private var projectionMatrix:Matrix3D;
private var myAngle:Number = 0;

public function PolarCube():void {

init();
}

private function init():void {
//Instantiate parameters
paraVec = new Vector3D();
verts = new Vector.<Number>();
indices= new Vector.<int>();
projectedVerts = new Vector.<Number>();
uvtData = new Vector.<Number>();
uvtShadingData = new Vector.<Number>();
bitmapData = new YourImage().bitmapData;

//remove primitive from stage before adding the next one
if(primitive){
removeChild(primitive);}
addChild(primitive= new Sprite());
//Set position on stage
primitive.x=stage.stageWidth/2;
primitive.y=stage.stageHeight/2;
//Set perspective
perspective= new PerspectiveProjection();
perspective.fieldOfView = 35.0; // camera angle, in degrees
var myConv:Number=Math.PI/180;
var varMyrIs:Number;
//Calculate Vertices
for (var i:int = 0 ; i!=rows; i++) {
ix= i/(rows-1)*Math.PI*2.0;

for (var j:int = 0 ; j!=cols; j++) {
iy= (j/(cols-1)-0.5)*Math.PI;

// 8 planes 8cases

if(ix>=0 && ix<2*Math.PI/4){
if(iy>=-2*Math.PI/4 && iy<0){
varMyrIs=rFunc(-1,-1,-1,ix,iy);
//θ: 0-90, Φ: 0-90, (1,1,1)
// trace(“0-90 0-90 “+varMyrIs);
}else{

varMyrIs=rFunc(1,1,1,ix,iy);
//θ: 0-90, Φ: 90-180, (-1,1,1)
//trace(“0-90 90-180 “+varMyrIs);

}
}

if(ix>=2*Math.PI/4 && ix<2*Math.PI/2){

if(iy>=-2*Math.PI/4 && iy<0){
varMyrIs=rFunc(-1,-1,1,ix,iy);
//θ: 90-180, Φ: 0-90, (1,1,-1)
//trace(“90-180 0-90 “+varMyrIs);
}else{
varMyrIs=rFunc(1,1,-1,ix,iy);
//θ: 90-180, Φ: 90-180, (-1,1,-1)
//trace(“90-180 90-180 “+varMyrIs);
}
}

if(ix>=2*Math.PI/2 && ix<6*Math.PI/4){

if(iy>=-2*Math.PI/4 && iy<0){
varMyrIs=rFunc(-1,1,1,ix,iy);
//θ: 180-270, Φ: 0-90, (1,-1,1)
trace(“180-270 0-90 “+varMyrIs);
}else{
varMyrIs=rFunc(1,-1,-1,ix,iy);
//θ: 180-270, Φ: 90-180, (-1,-1,1)
//trace(“180-270 90-180 “+varMyrIs);
}
}

if(ix>=6*Math.PI/4 && ix<=2*Math.PI){
trace(ix+” ix,”+iy+” iy,”);
if(iy>=-2*Math.PI/4 && iy<0){
varMyrIs=rFunc(-1,1,-1,ix,iy);
//θ: 270-360, Φ: 0-90, (1,-1,-1)
trace(“270-360 0-90 “+varMyrIs);
}else{
varMyrIs=rFunc(1,-1,1,ix,iy);
//θ: 270-360, Φ: 90-180, (-1,-1,-1)
trace(“270-360 90-180 “+varMyrIs);
}
}

//Sphere
paraVec = new Vector3D(
varMyrIs*Math.cos(iy)*Math.cos(ix), varMyrIs*Math.sin(iy), varMyrIs*Math.cos(iy)*Math.sin(ix));

//Collect vetices in the verts Vector array
verts.push(radx*paraVec.x,rady*paraVec.y,radz*paraVec.z);

//Load uvt data
uvtData.push( i/(rows-1),j/(cols-1), 0.0);
//Initialize projected vertices
projectedVerts.push(0.0,0.0);
}
}

//Create indices
var ii:int =0;
for (var ik:int=0 ; ik!=rows-1; ik++) {
for (var jk:int=0 ; jk!=cols-1; jk++) {
indices.push( ii,ii+cols+1,ii+1,
ii+cols,ii+cols+1,ii++);
}
ii++;
}

stage.addEventListener(Event.ENTER_FRAME, loop);
}

private function rFunc(a:Number, b:Number, c:Number, theta:Number, phi:Number):Number{

var myNum:Number=a/(Math.sin(phi)+b*Math.sin(theta)*Math.cos(phi)+c*Math.cos(theta)*Math.cos(phi));

return myNum;

//Math.cos(iy)*Math.cos(ix), Math.sin(iy), Math.cos(iy)*Math.sin(ix));

}

private function loop(event:Event =null):void {
// Set up a viewpoint:
projectionMatrix = perspective.toMatrix3D();
projectionMatrix.prependTranslation(0,0,.2);
myAngle+=2;
//Rotate Primtive
projectionMatrix.prependRotation( myAngle ,new Vector3D(0,1,0.0));
//Update projection vectors
Utils3D.projectVectors(projectionMatrix, verts,
projectedVerts, uvtData);
//Draw primtive
with (primitive.graphics) {
clear();
beginBitmapFill(bitmapData,null, false, false);
drawTriangles(projectedVerts, indices, uvtData,TriangleCulling.NEGATIVE);
endFill();
}
}

}
}

Advertisements

4 Responses to Flash CS4 Polar Cube (one-to-one mapping)

  1. makc says:

    the thing you have made here is not a cube, but its dual polyhedron – <boctahedron. this does not defeat the purpose, just pointing out the term ;)

  2. makc says:

    it’s “octahedron” up there, your blog ate my b-tags.

  3. […] The code sorts points into unique vertices and uv numbers (creating a 0ne-to-one mapping). If unique data can not be found new vertices are assigned creating a 0ne-to-one mapping as described in the post on the polar cube. […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: