/* Copyright (C) 2001 Damir Zucic */

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

				rotate_atom.c

Purpose:
	Rotate the given atom about the given axis. The axis is defined
	by two points (vectors). The vector from the first point to the
	second  point  defines  the axis  direction.  The  right-handed
	rotation  is positive and the left-handed rotation is negative.

Input:
	(1) Pointer to AtomS structure.
	(2) Pointer to VectorS structure (the first point).
	(3) Pointer to VectorS structure (the second point).
	(4) The rotation angle.

Output:
	(1) Atom position changed.
	(2) Return value.

Return value:
	(1) Positive on success.
	(2) 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:=================================================*/
 
double		AbsoluteValue_ (VectorS *);
int		ParallelPart_ (VectorS *, VectorS *, VectorS *);
void		VectorProduct_ (VectorS *, VectorS *, VectorS *);
double		ScalarProduct_ (VectorS *, VectorS *);

/*======rotate atom about given axis:========================================*/

int RotateAtom_ (AtomS *atomSP,
		 VectorS *vector1SP, VectorS *vector2SP, double angle)
{
VectorS		rot_axis_vectorS;
double		abs_value;
VectorS		radius_vectorS;
VectorS		parallel_vectorS;
VectorS		perpendicular_vectorS;
double		perpendicular_part;
double		reciprocal_abs_value;
VectorS		unit_vector1S, unit_vector2S;
double		cos_angle, sin_angle;
double		p1_new, p2_new;

/* Prepare the vector which defines the rotation axis: */
rot_axis_vectorS.x = vector2SP->x - vector1SP->x;
rot_axis_vectorS.y = vector2SP->y - vector1SP->y;
rot_axis_vectorS.z = vector2SP->z - vector1SP->z;

/* Check the absolute value of the rotation axis vector: */
abs_value = AbsoluteValue_ (&rot_axis_vectorS);
if (abs_value <= 0.0) return -1;

/* Prepare the radius vector of the given atom: */
radius_vectorS.x = atomSP->raw_atomS.x[0] - vector1SP->x;
radius_vectorS.y = atomSP->raw_atomS.y    - vector1SP->y;
radius_vectorS.z = atomSP->raw_atomS.z[0] - vector1SP->z;

/* Find the part of the radius vector which is parallel to the rotation */
/* axis.  Note that  we need the vector,  not just  the absolute value! */
ParallelPart_ (&parallel_vectorS, &rot_axis_vectorS, &radius_vectorS);

/* Find the part of the radius vector which */
/* is perpendicular  to the  rotation axis: */
perpendicular_vectorS.x = radius_vectorS.x - parallel_vectorS.x;
perpendicular_vectorS.y = radius_vectorS.y - parallel_vectorS.y;
perpendicular_vectorS.z = radius_vectorS.z - parallel_vectorS.z;

/* Prepare and check the absolute value of the perpendicular part: */
perpendicular_part = AbsoluteValue_ (&perpendicular_vectorS);
if (perpendicular_part <= 0.0) return -2;

/* Prepare the first unit vector, required for rotation: */
reciprocal_abs_value = 1.0 / perpendicular_part;
unit_vector1S.x = reciprocal_abs_value * perpendicular_vectorS.x;
unit_vector1S.y = reciprocal_abs_value * perpendicular_vectorS.y;
unit_vector1S.z = reciprocal_abs_value * perpendicular_vectorS.z;

/* Prepare and check the second unit vector: */
VectorProduct_ (&unit_vector2S, &rot_axis_vectorS, &unit_vector1S);
abs_value = AbsoluteValue_ (&unit_vector2S);
if (abs_value <= 0.0) return -3;
reciprocal_abs_value = 1.0 / abs_value;
unit_vector2S.x = reciprocal_abs_value * unit_vector2S.x;
unit_vector2S.y = reciprocal_abs_value * unit_vector2S.y;
unit_vector2S.z = reciprocal_abs_value * unit_vector2S.z;

/* Rotate the perpendicular vector: */
cos_angle = cos (angle);
sin_angle = sin (angle);
p1_new = perpendicular_part * cos_angle;
p2_new = perpendicular_part * sin_angle;
perpendicular_vectorS.x = p1_new * unit_vector1S.x + p2_new * unit_vector2S.x;
perpendicular_vectorS.y = p1_new * unit_vector1S.y + p2_new * unit_vector2S.y;
perpendicular_vectorS.z = p1_new * unit_vector1S.z + p2_new * unit_vector2S.z;

/* Update the radius vector: */
radius_vectorS.x = parallel_vectorS.x + perpendicular_vectorS.x;
radius_vectorS.y = parallel_vectorS.y + perpendicular_vectorS.y;
radius_vectorS.z = parallel_vectorS.z + perpendicular_vectorS.z;

/* Update the atomic coordinates: */
atomSP->raw_atomS.x[0] = radius_vectorS.x + vector1SP->x;
atomSP->raw_atomS.y    = radius_vectorS.y + vector1SP->y;
atomSP->raw_atomS.z[0] = radius_vectorS.z + vector1SP->z;

/* Return positive value (success indicator): */
return 1;
}

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


