/******************************************************************************************
 * Projector.java version 1.7                                                 Nov  8 1996 *
 *                                                                                        *
 * Copyright (c) 1996 Yanto Suryono. All Rights Reserved.                                 *
 *                                                                                        *
 * Permission to use, copy, and distribute this software for NON-COMMERCIAL purposes      *
 * and without fee is hereby granted provided that this copyright notice appears in all   *
 * copies and this software is not modified in any way                                    *
 *                                                                                        *
 * Please send bug reports and/or corrections/suggestions to                              *
 * Yanto Suryono <d0771@cranesv.egg.kushiro-ct.ac.jp>                                     *
 ******************************************************************************************/

//import java.awt.*;
import java.lang.*;
import javax.swing.*;
import java.awt.Rectangle;
import java.awt.Point;

public class Projector {
    private float scale_x, scale_y, scale_z;       // 3D scaling factor
    private float distance;                        // distance to object
    private float _2D_scale;                       // 2D scaling factor
    private float rotation, elevation;             // rotation and elevation angle
    private float sin_rotation, cos_rotation;      // sin and cos of rotation angle
    private float sin_elevation, cos_elevation;    // sin and cos of elevation angle
    private int   _2D_trans_x, _2D_trans_y;        // 2D translation
    private int   x1, x2, y1, y2;                  // projection area 
    private int   center_x, center_y;              // center of projection area           
    
    private int   trans_x, trans_y;
    private float factor;
    private float sx_cos, sy_cos, sz_cos;
    private float sx_sin, sy_sin, sz_sin;
    
    private final float DEGTORAD  = (float)Math.PI / 180;
    
    Projector() {
	setScaling(1); 
	setRotationAngle(0); 
	setElevationAngle(0); 
	setDistance(10);
	set2DScaling(1);
	set2DTranslation(0,0);
    }
    
    public void setProjectionArea(Rectangle r) {
	x1 = r.x; x2 = x1 + r.width;
	y1 = r.y; y2 = y1 + r.height;
	center_x = (x1 + x2) / 2;
	center_y = (y1 + y2) / 2;        
	
	trans_x = center_x + _2D_trans_x;
	trans_y = center_y + _2D_trans_y;
    }
    
    public void setRotationAngle(float angle) {
	rotation = angle;
	sin_rotation = (float)Math.sin(angle * DEGTORAD);
	cos_rotation = (float)Math.cos(angle * DEGTORAD);
	
	sx_cos = -scale_x * cos_rotation;
	sx_sin = -scale_x * sin_rotation;
	sy_cos = -scale_y * cos_rotation;
	sy_sin =  scale_y * sin_rotation;
    }
    
    public float getRotationAngle() {
	return rotation;
    }
    
    public float getSinRotationAngle() {
	return sin_rotation;
    }
    
    public float getCosRotationAngle() {
	return cos_rotation;
    }
    
    public void setElevationAngle(float angle) {
	elevation = angle;
	sin_elevation = (float)Math.sin(angle * DEGTORAD);
	cos_elevation = (float)Math.cos(angle * DEGTORAD);
	sz_cos =  scale_z * cos_elevation;
	sz_sin =  scale_z * sin_elevation;
    }
    
    public float getElevationAngle() {
	return elevation;
    }
    
    public float getSinElevationAngle() {
	return sin_elevation;
    }
    
    public float getCosElevationAngle() {
	return cos_elevation;
    }
    
    public void setDistance(float new_distance) {
	distance = new_distance;
	factor = distance * _2D_scale;
    }
    
    public float getDistance() {
	return distance;
    }
    
    public void setXScaling(float scaling) {
	scale_x = scaling;
	sx_cos = -scale_x * cos_rotation;
	sx_sin = -scale_x * sin_rotation;
    }
    
    public float getXScaling() {
	return scale_x;
    }
    
    public void setYScaling(float scaling) {
	scale_y = scaling;
	sy_cos = -scale_y * cos_rotation;
	sy_sin =  scale_y * sin_rotation;
    }
    
    public float getYScaling() {
	return scale_y;
    }
    
    public void setZScaling(float scaling) {
	scale_z = scaling;
	sz_cos =  scale_z * cos_elevation;
	sz_sin =  scale_z * sin_elevation;
    }
    
    public float getZScaling() {
	return scale_z;
    }
    
    public void setScaling(float x, float y, float z) {
	scale_x = x; scale_y = y; scale_z = z;
	
	sx_cos = -scale_x * cos_rotation;
	sx_sin = -scale_x * sin_rotation;
	sy_cos = -scale_y * cos_rotation;
	sy_sin =  scale_y * sin_rotation;
	sz_cos =  scale_z * cos_elevation;
	sz_sin =  scale_z * sin_elevation;
    }
    
    public void setScaling(float scaling) {
	scale_x = scale_y = scale_z = scaling;
	
	sx_cos = -scale_x * cos_rotation;
	sx_sin = -scale_x * sin_rotation;
	sy_cos = -scale_y * cos_rotation;
	sy_sin =  scale_y * sin_rotation;
	sz_cos =  scale_z * cos_elevation;
	sz_sin =  scale_z * sin_elevation;
    }
    
    public void set2DScaling(float scaling) {
	_2D_scale = scaling;
	factor = distance * _2D_scale;
    }
    
    public float get2DScaling() {
	return _2D_scale;
    }
    
    public void set2DTranslation(int x, int y) {
	_2D_trans_x = x; _2D_trans_y = y;
	
	trans_x = center_x + _2D_trans_x;
	trans_y = center_y + _2D_trans_y;
    }
    
    public void set2D_xTranslation(int x) {
	_2D_trans_x = x;
	trans_x = center_x + _2D_trans_x;
    }
    
    public int get2D_xTranslation() {
	return _2D_trans_x;
    }
    
    public void set2D_yTranslation(int y) {
	_2D_trans_y = y;
	trans_y = center_y + _2D_trans_y;
    }
    
    public int get2D_yTranslation() {
	return _2D_trans_y;
    }
    
    public Point project(float x, float y, float z) {
	float temp;
	
	// rotate 
	
	temp = x; 
	x = x * sx_cos + y * sy_sin;
	y = temp * sx_sin + y * sy_cos;
	
	// elevate and project
	
	temp = factor / (y * cos_elevation - z * sz_sin + distance);
	return new Point((int)(Math.round(x * temp) + trans_x),
			 (int)(Math.round((y * sin_elevation + z * sz_cos) * -temp) + trans_y));
    }
}
