/* Copyright 2003  Alexander V. Diemand

    This file is part of MolTalk.

    MolTalk 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; either version 2 of the License, or
    (at your option) any later version.

    MolTalk 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 MolTalk; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
 
/* vim: set filetype=objc: */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>


#include "Coordinates.oh"
#include "Matrix53.oh"
#include "Matrix44.oh"



@implementation Coordinates


-(id)init	//@nodoc
{
	[super setDimensions: 4];
	[self atDim: 0 value: 0.0];
	[self atDim: 1 value: 0.0];
	[self atDim: 2 value: 0.0];
	[self atDim: 3 value: 1.0];
	return self;
}


-(void)dealloc	//@nodoc
{
	[super dealloc];
}


/*
 *   calculates distance between the two coordinates
 */
-(double)distanceTo:(Coordinates*)c2
{
	return [self euklideanDistanceTo: c2];
}

/*
 *   calculate distance of this coordinates to the line given by (v2,v3)
 */
-(double)distanceToLineFrom:(Coordinates*)v2 to:(Coordinates*)v3
{
/*
 *              D
 *              |
 *         P    |A
 *     ----x----x------------ plane
 *              |
 *              |
 *              O
 *  Abbildung von OP auf OD(Normale): OD . OP / |OD| => h
 *  Streckung von OD mit h ergiebt OA
 *  Distanz ist gegeben von A zu P   []
 */
	/* calculate plane through this point with normal: v3-v2 */
	Coordinates *normal = [v2 differenceTo: v3];
	Coordinates *base = [v2 differenceTo: self];
	double h = [base scalarProductBy: normal]/[normal length];
	[[[normal normalize] scaleByScalar: h] add:v2];
	return [self distanceTo: normal];
}


/*
 *   overwrite so we won't add up all 4 dimensions but only the 3 real ones 
 */
-(double)euklideanDistanceTo:(Vector*)v2
{
	double dist = 0.0;
	double t;
	int i;
	for (i=0; i<3; i++) // only first 3 dimensions
	{
		t = [v2 atDim: i] - [self atDim: i];
		dist += (t * t);
	}
	return sqrt(dist);
}


/*
 *   set coordinates to the new values
 */
-(id)setX:(double)newx Y:(double)newy Z:(double)newz
{
	[self atDim: 0 value: newx];
	[self atDim: 1 value: newy];
	[self atDim: 2 value: newz];
	return self;
}


-(double)x
{
	return [self atDim: 0];
}


-(double)y
{
	return [self atDim: 1];
}


-(double)z
{
	return [self atDim: 2];
}


-(id)rotateBy:(Matrix44*)m
{
	/*         0  1  2  3
	 * 0     |r1 r2 r3 t1|      |x|
	 * 1     |r4 r5 r6 t2|      |y|
	 * 2 M = |r7 r8 r9 t3|  v = |z|
	 * 3     |o1 o2 o3 1 |      |1|
	 *
	 *           |r1*x+r2*y+r3*z+t1*1|
	 *           |r4*x+r5*x+r6*z+t2*1|
	 * v' = Mv = |r7*x+r8*x+r9*z+t3*1|
	 *           |o1*x+o2*x+o3*z+1*1 |
	 *
	 */
	double x,y,z;
	double t1,t2,t3;
	double r1,r2,r3,r4,r5,r6,r7,r8,r9;
	x=[self x]-[m atRow:3 col:0]; y=[self y]-[m atRow:3 col:1]; z=[self z]-[m atRow:3 col:2];
	t1=[m atRow:0 col:3]; t2=[m atRow:1 col:3]; t3=[m atRow:2 col:3];
	r1=[m atRow:0 col:0]; r2=[m atRow:0 col:1]; r3=[m atRow:0 col:2];
	r4=[m atRow:1 col:0]; r5=[m atRow:1 col:1]; r6=[m atRow:1 col:2];
	r7=[m atRow:2 col:0]; r8=[m atRow:2 col:1]; r9=[m atRow:2 col:2];
	[self setX:(r1*x+r2*y+r3*z+t1)
	 	 Y:(r4*x+r5*y+r6*z+t2)
	 	 Z:(r7*x+r8*y+r9*z+t3)];
	return self;
}


-(id)transformBy:(Matrix53*)m
{
	/*         0  1  2
	 * 0     |r1 r2 r3|      |x|
	 * 1     |r4 r5 r6|      |y|
	 * 2 M = |r7 r8 r9|  v = |z|
	 * 3     |o1 o2 o3|      |1|
	 * 4     |t1 t2 t3|
	 *
	 *           |r1*x+r2*y+r3*z+t1*1|
	 *           |r4*x+r5*x+r6*z+t2*1|
	 * v' = Mv = |r7*x+r8*x+r9*z+t3*1|
	 *           |o1*x+o2*x+o3*z+1*1 |
	 *
	 */
	double x,y,z;
	double t1,t2,t3;
	double r1,r2,r3,r4,r5,r6,r7,r8,r9;
	x=[self x]-[m atRow:3 col:0]; y=[self y]-[m atRow:3 col:1]; z=[self z]-[m atRow:3 col:2];
	t1=[m atRow:4 col:0]; t2=[m atRow:4 col:1]; t3=[m atRow:4 col:2];
	r1=[m atRow:0 col:0]; r2=[m atRow:0 col:1]; r3=[m atRow:0 col:2];
	r4=[m atRow:1 col:0]; r5=[m atRow:1 col:1]; r6=[m atRow:1 col:2];
	r7=[m atRow:2 col:0]; r8=[m atRow:2 col:1]; r9=[m atRow:2 col:2];
	[self setX:(r1*x+r2*y+r3*z+t1)
	 	 Y:(r4*x+r5*y+r6*z+t2)
	 	 Z:(r7*x+r8*y+r9*z+t3)];
	return self;
}


/*
 *   create new coordinates at the origin (0,0,0)
 */
+(Coordinates*)origin
{
	Coordinates *origin = [[self alloc] init];
	return AUTORELEASE(origin);
}


/*
 *   create new coordinates with the given values
 */
+(Coordinates*)coordsWithX:(double)p_x Y:(double)p_y Z:(double)p_z
{
	Coordinates *coords = [[self alloc] init];
	[coords setX: p_x Y:p_y Z:p_z];
	return AUTORELEASE(coords);
}

/*
 *   make copy of given coordinates
 */
+(Coordinates*)coordsFromCoordinates:(Coordinates*)p_coords
{
	Coordinates *coords = [[self alloc] init];
	[coords setX:[p_coords x]  Y:[p_coords y] Z:[p_coords z]];
	return AUTORELEASE(coords);
}


@end
 
