/* Copyright (C) 2000, 2001 Damir Zucic */

/*=============================================================================

				align_normal.c

Purpose:
	Align normal vector of the specified complex with the specified axis.
	The entire  macromolecular complex  will be rotated.  The membrane is 
	not properly rotated!

Input:
	(1) Pointer to MolComplexS structure.
	(2) Pointer to ConfigS structure.
	(3) Axis identifier (1 = x, 2 = y, 3 = z).
	(4) Orientation identifier (-1 = antiparallel, 1 = parallel).

Output:
	(1) The macromolecular complex will be rotated.
	(2) Return value.

Return value:
	(1) Positive on success.
	(2) Zero if no alignment is necessary.
	(3) Negative on failure.

========includes:============================================================*/

#include <stdio.h>

#include <math.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>

#include "defines.h"
#include "typedefs.h"

/*======function prototypes:=================================================*/

void		VectorProduct_ (VectorS *, VectorS *, VectorS *);
double		AbsoluteValue_ (VectorS *);
double		ScalarProduct_ (VectorS *, VectorS *);
int		PrepareStereoData_ (MolComplexS *, ConfigS *);

/*======align normal vector with given axis:=================================*/

int AlignNormalVector_ (MolComplexS *mol_complexSP, ConfigS *configSP,
			int axisID, int orientationID)
{
size_t		atomsN, atomI;
VectorS		normal_vectorS;
VectorS		target_vectorS;
VectorS		rotation_axis_vectorS;
double		a1, a2;
double		angle, cos_angle, sin_angle;
double		absolute_value, reciprocal_abs_value;
VectorS		unit_vector1S, unit_vector2S, unit_vector3S;
double		cos_stereo_angle, sin_stereo_angle;
double		x0, y0, z0;
AtomS		*curr_atomSP;
VectorS		radius_vectorS;
double		projection1, projection2;
PlaneS		*curr_planeSP;
double		x1, z1, x, z, x_new, z_new;

/* Check the pointer to the macromolecular complex: */
if (!mol_complexSP) return -1;

/* Are there any atoms at all? */
atomsN = mol_complexSP->atomsN;
if (atomsN == 0) return -2;

/* Copy the normal vector: */
normal_vectorS.x = mol_complexSP->planeS.normal_x[0];
normal_vectorS.y = mol_complexSP->planeS.normal_y;
normal_vectorS.z = mol_complexSP->planeS.normal_z[0];

/* Prepare  the target vector.  After rotation  the normal */
/* vector will be parallel or antiparallel to this vector: */
switch (axisID)
	{
	/* x axis: */
	case 1:
		target_vectorS.x = 1;
		target_vectorS.y = 0;
		target_vectorS.z = 0;
		break;

	/* y axis: */
	case 2:
		target_vectorS.x = 0;
		target_vectorS.y = 1;
		target_vectorS.z = 0;
		break;

	/* z axis: */
	case 3:
		target_vectorS.x = 0;
		target_vectorS.y = 0;
		target_vectorS.z = 1;
		break;

	default:
		return -3;
	}

/* Check the target orientation: */
if (orientationID == -1)
	{
	target_vectorS.x *= -1;
	target_vectorS.y *= -1;
	target_vectorS.z *= -1;
	}
else if (orientationID != 1) return -4;

/* The vector which defines the rotation axis: */
VectorProduct_ (&rotation_axis_vectorS, &normal_vectorS, &target_vectorS);

/* Prepare the rotation angle  and cosine and sine of this */
/* angle. Right-handed rotation is positive. Remember that */
/* arc cosine is very sensitive to  floating point errors: */
a1 = AbsoluteValue_ (&normal_vectorS);
a2 = AbsoluteValue_ (&target_vectorS);
if ((a1 == 0.0) || (a2 == 0.0)) return -5;
cos_angle = ScalarProduct_ (&normal_vectorS, &target_vectorS) / (a1 * a2);
if (cos_angle <= -1.0) angle = 3.1415927;
else if (cos_angle >= 1.0) angle = 0.0;
else angle = acos (cos_angle);
sin_angle = sin (angle);

/* Prepare and check the absolute value of the rotation axis vector: */
absolute_value = AbsoluteValue_ (&rotation_axis_vectorS);
if (absolute_value == 0.0) return -6;

/* Prepare the unit vector which defines the rotation axis: */
reciprocal_abs_value = 1.0 / absolute_value;
unit_vector1S.x = rotation_axis_vectorS.x * reciprocal_abs_value;
unit_vector1S.y = rotation_axis_vectorS.y * reciprocal_abs_value;
unit_vector1S.z = rotation_axis_vectorS.z * reciprocal_abs_value;

/* Calculate cosine and sine of the stereo angle: */
cos_stereo_angle = cos (configSP->stereo_angle);
sin_stereo_angle = sin (configSP->stereo_angle);

/* Copy the rotation center coordinates: */
x0 = mol_complexSP->rotation_center_vectorS.x;
y0 = mol_complexSP->rotation_center_vectorS.y;
z0 = mol_complexSP->rotation_center_vectorS.z;

/* Rotate every atom: */
for (atomI = 0; atomI < atomsN; atomI++)
	{
	/* Pointer to the current atom: */
	curr_atomSP = mol_complexSP->atomSP + atomI;

	/* Coordinates relative to the rotation center: */
	radius_vectorS.x = curr_atomSP->raw_atomS.x[0] - x0;
	radius_vectorS.y = curr_atomSP->raw_atomS.y    - y0;
	radius_vectorS.z = curr_atomSP->raw_atomS.z[0] - z0;

	/* Prepare the third unit vector (the second is prepared last): */
	VectorProduct_ (&unit_vector3S, &unit_vector1S, &radius_vectorS);

	/* If the absolute value is equal to zero it is */
	/* not necessary  to rotate  the radius vector: */
	absolute_value = AbsoluteValue_ (&unit_vector3S);
	if (absolute_value == 0.0) continue;

	/* Normalize the third unit vector: */
	reciprocal_abs_value = 1.0 / absolute_value;
	unit_vector3S.x = unit_vector3S.x * reciprocal_abs_value;
	unit_vector3S.y = unit_vector3S.y * reciprocal_abs_value;
	unit_vector3S.z = unit_vector3S.z * reciprocal_abs_value;

	/* Prepare the second unit vector: */
	VectorProduct_ (&unit_vector2S, &unit_vector3S, &unit_vector1S);

	/* Two projections of the radius vector: */
	projection1 = ScalarProduct_ (&radius_vectorS, &unit_vector1S);
	projection2 = ScalarProduct_ (&radius_vectorS, &unit_vector2S);

	/* Rotate the radius vector: */
	radius_vectorS.x = projection1 * unit_vector1S.x +
			   projection2 * unit_vector2S.x * cos_angle +
			   projection2 * unit_vector3S.x * sin_angle;
	radius_vectorS.y = projection1 * unit_vector1S.y +
			   projection2 * unit_vector2S.y * cos_angle +
			   projection2 * unit_vector3S.y * sin_angle;
	radius_vectorS.z = projection1 * unit_vector1S.z +
			   projection2 * unit_vector2S.z * cos_angle +
			   projection2 * unit_vector3S.z * sin_angle;

	/* Translate and store new coordinates: */
	curr_atomSP->raw_atomS.x[0] = radius_vectorS.x + x0;
	curr_atomSP->raw_atomS.y    = radius_vectorS.y + y0;
	curr_atomSP->raw_atomS.z[0] = radius_vectorS.z + z0;
	}

/* Prepare stereo data, if required: */
if (configSP->stereoF) PrepareStereoData_ (mol_complexSP, configSP);

/* Set the position_changedF: */
mol_complexSP->position_changedF = 1;

/* Prepare the pointer to the current plane: */
curr_planeSP = &mol_complexSP->planeS;

/* Rotate the plane center: */

/* Position of the plane center relative to the rotation center: */
radius_vectorS.x = curr_planeSP->center_x[0] - x0;
radius_vectorS.y = curr_planeSP->center_y    - y0;
radius_vectorS.z = curr_planeSP->center_z[0] - z0;

/* Prepare the third unit vector: */
VectorProduct_ (&unit_vector3S, &unit_vector1S, &radius_vectorS);

/* If the absolute value is different from zero */
/* it is necessary to rotate the radius vector: */
absolute_value = AbsoluteValue_ (&unit_vector3S);
if (absolute_value != 0.0)
	{
	/* Normalize the third unit vector: */
	reciprocal_abs_value = 1.0 / absolute_value;
	unit_vector3S.x = unit_vector3S.x * reciprocal_abs_value;
	unit_vector3S.y = unit_vector3S.y * reciprocal_abs_value;
	unit_vector3S.z = unit_vector3S.z * reciprocal_abs_value;

	/* The second unit vector: */
	VectorProduct_ (&unit_vector2S, &unit_vector3S, &unit_vector1S);

	/* Two projections of the radius vector: */
	projection1 = ScalarProduct_ (&radius_vectorS, &unit_vector1S);
	projection2 = ScalarProduct_ (&radius_vectorS, &unit_vector2S);

	/* Rotate the radius vector: */
	radius_vectorS.x = projection1 * unit_vector1S.x +
			   projection2 * unit_vector2S.x * cos_angle +
			   projection2 * unit_vector3S.x * sin_angle;
	radius_vectorS.y = projection1 * unit_vector1S.y +
			   projection2 * unit_vector2S.y * cos_angle +
			   projection2 * unit_vector3S.y * sin_angle;
	radius_vectorS.z = projection1 * unit_vector1S.z +
			   projection2 * unit_vector2S.z * cos_angle +
			   projection2 * unit_vector3S.z * sin_angle;

	/* Translate and store the rotated plane center: */
	curr_planeSP->center_x[0] = radius_vectorS.x + x0;
	curr_planeSP->center_y    = radius_vectorS.y + y0;
	curr_planeSP->center_z[0] = radius_vectorS.z + z0;

	/* Prepare stereo data for the plane center: */
	x1 = mol_complexSP->geometric_center_vectorS.x;
	z1 = mol_complexSP->geometric_center_vectorS.z;
	x = curr_planeSP->center_x[0] - x1;
	z = curr_planeSP->center_z[0] - z1;
	x_new =  x * cos_stereo_angle + z * sin_stereo_angle;
	z_new = -x * sin_stereo_angle + z * cos_stereo_angle;
	curr_planeSP->center_x[1] = x_new + x1;
	curr_planeSP->center_z[1] = z_new + z1;
	}

/* Rotate the normal vector: */

/* Prepare the third unit vector: */
VectorProduct_ (&unit_vector3S, &unit_vector1S, &normal_vectorS);

/* If the absolute value is zero there is */
/* no need  to rotate  the normal vector: */
absolute_value = AbsoluteValue_ (&unit_vector3S);
if (absolute_value == 0.0) return 1;

/* Normalize the third unit vector: */
reciprocal_abs_value = 1.0 / absolute_value;
unit_vector3S.x = unit_vector3S.x * reciprocal_abs_value;
unit_vector3S.y = unit_vector3S.y * reciprocal_abs_value;
unit_vector3S.z = unit_vector3S.z * reciprocal_abs_value;

/* The second unit vector: */
VectorProduct_ (&unit_vector2S, &unit_vector3S, &unit_vector1S);

/* Two projections of the radius vector: */
projection1 = ScalarProduct_ (&normal_vectorS, &unit_vector1S);
projection2 = ScalarProduct_ (&normal_vectorS, &unit_vector2S);

/* Rotate the radius vector: */
normal_vectorS.x = projection1 * unit_vector1S.x +
		   projection2 * unit_vector2S.x * cos_angle +
		   projection2 * unit_vector3S.x * sin_angle;
normal_vectorS.y = projection1 * unit_vector1S.y +
		   projection2 * unit_vector2S.y * cos_angle +
		   projection2 * unit_vector3S.y * sin_angle;
normal_vectorS.z = projection1 * unit_vector1S.z +
		   projection2 * unit_vector2S.z * cos_angle +
		   projection2 * unit_vector3S.z * sin_angle;

/* Translate and store the rotated normal vector: */
curr_planeSP->normal_x[0] = normal_vectorS.x;
curr_planeSP->normal_y    = normal_vectorS.y;
curr_planeSP->normal_z[0] = normal_vectorS.z;

/* Prepare stereo data for normal vector: */
curr_planeSP->normal_x[1] =  curr_planeSP->normal_x[0] * cos_stereo_angle +
			     curr_planeSP->normal_z[0] * sin_stereo_angle;
curr_planeSP->normal_z[1] = -curr_planeSP->normal_x[0] * sin_stereo_angle +
			     curr_planeSP->normal_z[0] * cos_stereo_angle;

/* Return positive value on success: */
return 2;
}

/*===========================================================================*/


