/*=============================================================================
|
| NAME
|
| TransformMatrix.java
|
| DESCRIPTION
|
| Set of 4 x 4 matrices for 3D homogeneous coordinate transforms,
| including rotation, translation, scaling and perspective.
|
| PUBLIC MEMBER FUNCTIONS
|
| TransformMatrix( ) Creates identity transform.
| TransformMatrix( m ) Copies the matrix m.
| get() Returns the entire matrix.
| getElement(i, j) Returns element in row i, column j.
| setElement(i, j) Returns element in row i, column j.
| multiply( m1, m2 ) Returns m1 * m2.
| multiply( v ) Return m * v.
| print() Debug prints the matrix.
|
| EXCEPTIONS
|
| Description
|
| NOTES
|
| Algorithm_References_Tricks
|
| BUGS
|
| Bugs_Enhancements
|
| AUTHOR
|
| Sean E. O'Connor
|
| LEGAL
|
| Prentice Version 1.1 - An Artist's Software Apprentice.
| Copyright (C) 2003-2008 by Sean Erik O'Connor. All Rights Reserved.
|
| This program is free software; you can redistribute it and/or
| modify it under the terms of the GNU General Public License
| as published by the Free Software Foundation; version 2
| of the License.
|
| This program is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU General Public License
| along with this program; if not, write to the Free Software
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
| The author's address is artifex@seanerikoconnor.freeservers.com.
|
+============================================================================*/
package Model ; // Package name for this project.
import java.text.* ; // DecimalFormat
import Prentice.* ; // Prentice stuff.
import Model.* ;
/**
* @see theory
* @author seanoconnor
*/
class TransformMatrix implements PrenticeConstants
{
// 4 x 4 matrix holding the homogeneous transform coordinates.
// Let it be accessed by subclasses for convenience.
protected double[][] matrix_ ;
//
// Default constructor creates zero transform matrix.
//
public TransformMatrix()
{
// Allocate the whole matrix.
matrix_ = new double[ TRANSFORM_MATRIX_SIZE ][ TRANSFORM_MATRIX_SIZE ] ;
// Zero it out.
for (int row = 0 ; row < TRANSFORM_MATRIX_SIZE ; ++row)
{
for (int col = 0 ; col < TRANSFORM_MATRIX_SIZE ; ++col)
{
matrix_[ row ][ col ] = 0 ;
}
}
}
//
// Default constructor which creates a copy of the matrix m.
//
public TransformMatrix( TransformMatrix m )
{
// Allocate a new matrix.
matrix_ = new double[ TRANSFORM_MATRIX_SIZE ][ TRANSFORM_MATRIX_SIZE ] ;
// Copy its elements.
for (int row = 0 ; row < TRANSFORM_MATRIX_SIZE ; ++row)
{
for (int col = 0 ; col < TRANSFORM_MATRIX_SIZE ; ++col)
{
matrix_[ row ][ col ] = m.get()[ row ][ col ] ;
}
}
}
//
// Return the entire matrix.
//
public double[][] get()
{
return matrix_ ;
}
//
// Return m element of the matrix.
// ij
public double getElement( int row, int col )
{
return matrix_[ row ][ col ] ;
}
//
// Set m element of the matrix.
// ij
public void setElement( int row, int col, double element )
{
matrix_[ row ][ col ] = element ;
}
//
// Matrix multiply.
//
public TransformMatrix multiply( TransformMatrix m1,
TransformMatrix m2 )
{
TransformMatrix productMatrix = new TransformMatrix() ;
// Multiply out.
for (int i = 0 ; i < TRANSFORM_MATRIX_SIZE ; ++i)
{
for (int k = 0 ; k < TRANSFORM_MATRIX_SIZE ; ++k)
{
double sum = 0.0 ;
for (int j = 0 ; j < TRANSFORM_MATRIX_SIZE ; ++j)
{
sum += (m1.get()[ i ][ j ] * m2.get()[ j ][ k ]) ;
}
productMatrix.matrix_[ i ][ k ] = sum ;
}
}
return productMatrix ;
}
//
// Matrix vector multiply.
//
public TransformVector multiply( TransformVector v )
{
TransformVector productVector = new TransformVector() ;
// Multiply out.
for (int row = 0 ; row < TRANSFORM_MATRIX_SIZE ; ++row)
{
double sum = 0.0 ;
for (int col = 0 ; col < TRANSFORM_MATRIX_SIZE ; ++col)
{
sum += matrix_[ row ][ col ] * v.get()[ col ] ;
}
productVector.get()[ row ] = sum ;
}
return productVector ;
}
//
// Print out the matrix.
//
public void print()
{
for (int row = 0 ; row < TRANSFORM_MATRIX_SIZE ; ++row)
{
DebugLog.print( "( " ) ;
for (int col = 0 ; col < TRANSFORM_MATRIX_SIZE ; ++col)
{
DecimalFormat floatFormat = new DecimalFormat( " 0.000E000;-0.000E000" ) ;
String num = floatFormat.format( matrix_[ row ][ col ] ) ;
DebugLog.print( num + " " ) ;
}
}
}
}
/*=============================================================================
|
| NAME
|
| PerspectiveMatrix
|
| DESCRIPTION
|
| Invertible perspective transformation.
|
| PUBLIC MEMBER FUNCTIONS
|
| PerspectiveMatrix()
| PerspectiveMatrix( double eyeToProjectionPlaneDistance )
|
| EXCEPTIONS
|
| Description
|
| NOTES
|
| BUGS
|
| Bugs_Enhancements
|
| AUTHOR
|
| Sean E. O'Connor
|
+============================================================================*/
class PerspectiveMatrix extends TransformMatrix
{
//
// Default projection in which d = 1 unit.
//
public PerspectiveMatrix()
{
// Construct zero transform matrix.
super() ;
// 1's along diagonal.
for (int row = 0 ; row < TRANSFORM_MATRIX_SIZE ; ++row)
{
matrix_[ row ][ row ] = 1.0 ;
}
// Distance from eye to projection plane = 1.
matrix_[ 3 ][ 2 ] = 1.0 ;
}
//
// Use all information about the perspective.
//
public PerspectiveMatrix( double eyeToProjectionPlaneDistance,
double picturePlaneWidth,
double picturePlaneHeight,
double FarPlaneDistance,
double NearPlaneDistance )
{
// Construct zero transform matrix.
super() ;
// 1's along diagonal.
for (int row = 0 ; row < TRANSFORM_MATRIX_SIZE ; ++row)
{
matrix_[ row ][ row ] = 1.0 ;
}
// TODO handle divide by zero.
matrix_[ 3 ][ 2 ] = 1.0 / eyeToProjectionPlaneDistance ;
}
}
/*=============================================================================
|
| NAME
|
| NameOfClass
|
| DESCRIPTION
|
| WhatItDoes
|
| PUBLIC MEMBER FUNCTIONS
|
| DefaultConstructor WhatItDoesBriefly
| ConstructorWithArguments WhatItDoesBriefly
| MemberFunction1 WhatItDoesBriefly
| MemberFunction2 WhatItDoesBriefly
| MemberFunction3 WhatItDoesBriefly
|
| EXCEPTIONS
|
| Description
|
| NOTES
|
| Algorithm_References_Tricks
|
| BUGS
|
| Bugs_Enhancements
|
| AUTHOR
|
| Sean E. O'Connor
|
+============================================================================*/
class RotationMatrix extends TransformMatrix
{
public RotationMatrix()
{
super() ;
}
public RotationMatrix( double angle )
{
super() ;
}
}
/*=============================================================================
|
| NAME
|
| TransformVector
|
| DESCRIPTION
|
| WhatItDoes
|
| PUBLIC MEMBER FUNCTIONS
|
| TransformVector
| setElement
| print
|
| EXCEPTIONS
|
| Description
|
| NOTES
|
| Algorithm_References_Tricks
|
| BUGS
|
| Bugs_Enhancements
|
| AUTHOR
|
| Sean E. O'Connor
|
+============================================================================*/
class TransformVector implements PrenticeConstants
{
// 4 vector holding the homogeneous transform coordinates.
// Let it be accessed by subclasses for convenience.
protected double[] vector_ ;
public TransformVector()
{
// Allocate the whole matrix.
vector_ = new double[ TRANSFORM_MATRIX_SIZE ] ;
// Zero it out.
for (int col = 0 ; col < TRANSFORM_MATRIX_SIZE ; ++col)
{
vector_[ col ] = 0 ;
}
}
// Set ith element of the matrix.
public void setElement( int col, double element )
{
vector_[ col ] = element ;
}
// Return a Java array.
public double[] get()
{
return vector_ ;
}
// Convert from 4D homogeneous coordinates to 3D coordinates.
public double[] to3D()
{
double[] vec3D = new double[ TRANSFORM_MATRIX_SIZE - 1 ] ;
vec3D[ 0 ] = vector_[ 0 ] / vector_[ 3 ] ;
vec3D[ 1 ] = vector_[ 1 ] / vector_[ 3 ] ;
vec3D[ 2 ] = vector_[ 2 ] / vector_[ 3 ] ;
return vec3D ;
}
// Print out the vector.
public void print()
{
DebugLog.print( "( " ) ;
for (int col = 0 ; col < TRANSFORM_MATRIX_SIZE ; ++col)
{
DecimalFormat floatFormat = new DecimalFormat( " 0.000E000;-0.000E000" ) ;
String num = floatFormat.format( vector_[ col ] ) ;
DebugLog.print( num + " " ) ;
}
}
}
/*=============================================================================
|
| NAME
|
| ClipView
|
| DESCRIPTION
|
| WhatItDoes
|
| PUBLIC MEMBER FUNCTIONS
|
| DefaultConstructor WhatItDoesBriefly
| ConstructorWithArguments WhatItDoesBriefly
| MemberFunction1 WhatItDoesBriefly
| MemberFunction2 WhatItDoesBriefly
| MemberFunction3 WhatItDoesBriefly
|
| EXCEPTIONS
|
| Description
|
| NOTES
|
| Algorithm_References_Tricks
|
| BUGS
|
| Bugs_Enhancements
|
| AUTHOR
|
| Sean E. O'Connor
|
+============================================================================*/
class ClipView implements PrenticeConstants
{
private double picturePlaneWidth_ ;
private double picturePlaneHeight_ ;
private double eyeToProjectionPlaneDistance_ ;
private double farPlaneDistance_ ;
private double nearPlaneDistance_ ;
// Create the clipping object.
public ClipView( double eyeToProjectionPlaneDistance,
double picturePlaneWidth,
double picturePlaneHeight,
double farPlaneDistance,
double nearPlaneDistance )
{
picturePlaneWidth_ = picturePlaneWidth ;
picturePlaneHeight_ = picturePlaneHeight ;
eyeToProjectionPlaneDistance_ = eyeToProjectionPlaneDistance ;
farPlaneDistance_ = farPlaneDistance ;
nearPlaneDistance_ = nearPlaneDistance ;
}
// Clip in homogeneous coordinates.
boolean clip( TransformVector tv )
{
double[] v = tv.get() ;
double x = tv.get()[ 0 ] ;
double y = tv.get()[ 1 ] ;
double z = tv.get()[ 2 ] ;
double w = tv.get()[ 3 ] ;
double t1 = w * eyeToProjectionPlaneDistance_ * picturePlaneHeight_ ;
double t2 = 2 * eyeToProjectionPlaneDistance_ * y ;
double t3 = picturePlaneHeight_ * z ;
double s1 = w * eyeToProjectionPlaneDistance_ * picturePlaneWidth_ ;
double s2 = 2 * eyeToProjectionPlaneDistance_ * x ;
double s3 = picturePlaneWidth_ * z ;
DebugLog.println( "-t1 + t2 - t3 = " + (-t1 + t2 - t3) ) ;
DebugLog.println( "-t1 - t2 - t3 = " + (-t1 - t2 - t3) ) ;
// Initially within the viewing pyramid.
boolean clipping = false ;
// Clip when higher than the top plane of the viewing pyramid.
if (-t1 + t2 - t3 > 0.0)
{
DebugLog.println( "Clipping against top plane" ) ;
clipping = true ;
}
// Clip when lower than the bottom plane of the viewing pyramid.
else if (-t1 -t2 -t3 > 0.0)
{
DebugLog.println( "Clipping against bottom plane" ) ;
clipping = true ;
}
// Clip when right of the right plane of the viewing pyramid.
else if (-s1 -s2 -s3 > 0.0)
{
DebugLog.println( "Clipping against right plane" ) ;
clipping = true ;
}
// Clip when left of the left plane of the viewing pyramid.
else if (-s1 +s2 -s3 > 0.0)
{
DebugLog.println( "Clipping against left plane" ) ;
clipping = true ;
}
// Clip when beyond far plane.
else if (z > w * farPlaneDistance_)
{
DebugLog.println( "Clipping against far plane" ) ;
clipping = true ;
}
// Clip when nearer than near plane.
else if (z < w * nearPlaneDistance_)
{
DebugLog.println( "Clipping against near plane" ) ;
clipping = true ;
}
// Change the sense for w < 0
if (w > 0)
return clipping ;
else
{
DebugLog.println( "w < 0: Reversing clip sense." ) ;
if (clipping == true)
clipping = false ;
else if (clipping == true)
clipping = true ;
}
return clipping ;
}
}
// Map a point on the picture plane to the screen, keeping aspect ratio constant.
class PicturePlaneToScreenTransform
{
private int screenWidth_ ;
private int screenHeight_ ;
private double picturePlaneWidth_ ;
private double picturePlaneHeight_ ;
public PicturePlaneToScreenTransform( int screenWidth,
int screenHeight,
double picturePlaneWidth,
double picturePlaneHeight )
{
// Save constructor arguments into object.
screenWidth_ = screenWidth ;
screenHeight_ = screenHeight ;
picturePlaneWidth_ = picturePlaneWidth ;
picturePlaneHeight_ = picturePlaneHeight ;
}
/**
*
* @param picturePlanePoint
* @return
*/
int [] map( double [] picturePlanePoint )
{
int [] screenPoint = new int[ 2 ] ;
// Compute aspect ratios for picture plane and screen.
double picturePlaneAspectRatio = picturePlaneWidth_ / picturePlaneHeight_ ;
double screenAspectRatio = (double) screenWidth_ /
(double) screenHeight_ ;
// Picture plane is taller and narrower than the display screen.
// Keep the picture plane aspect ratio invariant such that
// picturePlaneAspectRatio = / .
double scaledScreenWidth = 0.0 ;
double scaledScreenHeight = 0.0 ;
if (picturePlaneAspectRatio < screenAspectRatio)
{
scaledScreenWidth =
(int)(0.5 + screenHeight_ * picturePlaneAspectRatio) ;
scaledScreenHeight = screenHeight_ ;
}
// picturePlaneAspectRatio = / .
else
{
scaledScreenWidth = screenWidth_ ;
scaledScreenHeight =
(int)(0.5 + screenWidth_ / picturePlaneAspectRatio) ;
}
// Picture plane x axis is lefthanded, so invert first.
// Rescale picture plane width to screen width.
// Picture plane origin is at screen center, so translate.
double normalizedPicturePlaneX =
(-picturePlanePoint[ 0 ] / picturePlaneWidth_) *
(double) scaledScreenWidth ;
screenPoint[ 0 ] = (int)(0.5 + normalizedPicturePlaneX +
(double) scaledScreenWidth / 2.0) ;
// Same idea, but screen y goes downwards.
double normalizedPicturePlaneY =
(picturePlanePoint[ 1 ] / picturePlaneHeight_) *
(double) scaledScreenHeight ;
screenPoint[ 1 ] = (int)(0.5 - normalizedPicturePlaneY +
(double) scaledScreenHeight / 2.0) ;
return screenPoint ;
}
}