/* Copyright (C) 2000-2002 Damir Zucic */

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

				rotate.c

Purpose:
	Rotate all caught macromolecular complexes  or edit default complex.

Input:
	(1) Pointer to MolComplexS structure, with macromolecular complexes.
	(2) Number of macromolecular complexes.
	(3) Pointer to RuntimeS structure.
	(4) Pointer to ConfigS structure.
	(5) Rotation angle.
	(6) Rotation axis identifier (1 = x, 2 = y, 3 = z).

Output:
	(1) Atoms rotated in all caught macromolecular complexes.

Return value:
	(1) Edit mode index (positive or zero) 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:=================================================*/

int		EditPhi_ (MolComplexS *, RuntimeS *, ConfigS *, double);
int		EditPsi_ (MolComplexS *, RuntimeS *, ConfigS *, double);
int		EditOmega_ (MolComplexS *, RuntimeS *, ConfigS *, double);
int		EditBond_ (MolComplexS *, int, RuntimeS *, ConfigS *, double);
int		EditChi1_ (MolComplexS *, RuntimeS *, ConfigS *, double);
int		EditChi2_ (MolComplexS *, RuntimeS *, ConfigS *, double);
int		PrepareStereoData_ (MolComplexS *, ConfigS *);
void		RotatePlane_ (MolComplexS *, ConfigS *, double, int);
void		RotateMembrane_ (MolComplexS *, ConfigS *, double, int);

/*======rotate all caught macromol. complexes:===============================*/

int Rotate_ (MolComplexS *mol_complexSP, int mol_complexesN,
	      RuntimeS *runtimeSP, ConfigS *configSP,
	      double rotation_angle, int axisID)
{
MolComplexS	*curr_mol_complexSP;
int		edit_modeI;
int		mol_complexI;
double		cos_angle, sin_angle;
size_t		atomsN, atomI;
AtomS		*curr_atomSP;
double		x0, y0, z0, x, y, z, x_new = 0.0, y_new = 0.0, z_new = 0.0;

/* Pointer to default macromolecular complex: */
curr_mol_complexSP = mol_complexSP + runtimeSP->default_complexI;

/* Copy the edit mode index: */
edit_modeI = runtimeSP->edit_modeI;

/* Check the axis identifier and edit mode index. */

/* If axisID is three and edit_modeI is two, three, four or */
/* six,  edit phi, psi,  omega or selected side chain bond: */
if ((axisID == 3) &&
    ((edit_modeI == 2) || (edit_modeI == 3) ||
     (edit_modeI == 4) || (edit_modeI == 6))) 
	{
	/* Edit phi: */
	if (edit_modeI == 2)
		{
		EditPhi_ (curr_mol_complexSP,
			  runtimeSP, configSP,
			  rotation_angle);
		}

	/* Edit psi: */
	else if (edit_modeI == 3)
		{
		EditPsi_ (curr_mol_complexSP,
			  runtimeSP, configSP,
			  rotation_angle);
		}

	/* Edit omega: */
	else if (edit_modeI == 4)
		{
		EditOmega_ (curr_mol_complexSP,
			    runtimeSP, configSP,
			    rotation_angle);
		}

	/* Edit some other bond (side chain): */
	else if (edit_modeI == 6)
		{
		EditBond_ (curr_mol_complexSP,
			   runtimeSP->default_complexI,
			   runtimeSP, configSP,
			   rotation_angle);
		}

	/* Prepare the stereo data,  update one */
	/* flag and return the edit mode index: */
	/* Prepare stereo data, if required: */
	if (configSP->stereoF)
		{
		PrepareStereoData_ (curr_mol_complexSP, configSP);
		}

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

	/* Return the edit mode index: */
	return edit_modeI;
	}

/* If axis identifier is two and edit_modeI is five, edit phi: */
else if ((axisID == 2) && (edit_modeI == 5))
	{
	/* Edit phi: */
	rotation_angle *= -1;
	EditPhi_ (curr_mol_complexSP, runtimeSP, configSP, rotation_angle);

	/* Prepare the stereo data,  update one */
	/* flag and return the edit mode index: */
	/* Prepare stereo data, if required: */
	if (configSP->stereoF)
		{
		PrepareStereoData_ (curr_mol_complexSP, configSP);
		}

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

	/* Return the edit mode index: */
	return edit_modeI;
	}

/* If axis identifier is two and edit_modeI is seven, edit chi1: */
else if ((axisID == 2) && (edit_modeI == 7))
	{
	/* Edit chi1: */
	rotation_angle *= -1;
	EditChi1_ (curr_mol_complexSP, runtimeSP, configSP, rotation_angle);

	/* Prepare the stereo data,  update one */
	/* flag and return the edit mode index: */
	/* Prepare stereo data, if required: */
	if (configSP->stereoF)
		{
		PrepareStereoData_ (curr_mol_complexSP, configSP);
		}

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

	/* Return the edit mode index: */
	return edit_modeI;
	}

/* If axis identifier is one and edit_modeI is five, edit psi: */
else if ((axisID == 1) && (edit_modeI == 5))
	{
	/* Edit psi: */
	rotation_angle *= -1;
	EditPsi_ (curr_mol_complexSP, runtimeSP, configSP, rotation_angle);

	/* Prepare the stereo data,  update one */
	/* flag and return the edit mode index: */
	/* Prepare stereo data, if required: */
	if (configSP->stereoF)
		{
		PrepareStereoData_ (curr_mol_complexSP, configSP);
		}

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

	/* Return the edit mode index: */
	return edit_modeI;
	}

/* If axis identifier is one and edit_modeI is seven, edit chi2: */
else if ((axisID == 1) && (edit_modeI == 7))
	{
	/* Edit chi2: */
	rotation_angle *= -1;
	EditChi2_ (curr_mol_complexSP, runtimeSP, configSP, rotation_angle);

	/* Prepare the stereo data,  update one */
	/* flag and return the edit mode index: */
	/* Prepare stereo data, if required: */
	if (configSP->stereoF)
		{
		PrepareStereoData_ (curr_mol_complexSP, configSP);
		}

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

	/* Return the edit mode index: */
	return edit_modeI;
	}

/* If this point is reached, the entire structure should be rotated. */

/* Calculate cosine and sine of the rotation angle: */
cos_angle = cos (rotation_angle);
sin_angle = sin (rotation_angle);

/* If working in default mode, rotate the entire structure: */
for (mol_complexI = 0; mol_complexI < mol_complexesN; mol_complexI++)
	{
	/* Pointer to the current macromolecular complex: */
	curr_mol_complexSP = mol_complexSP + mol_complexI;

	/* Check is the current macromolecular complex caught: */
	if (curr_mol_complexSP->catchF == 0) continue;

	/* Check is it necessary to rotate the plane: */
	if (curr_mol_complexSP->move_bits & PLANE_MASK)
		{
		RotatePlane_ (curr_mol_complexSP, configSP,
			      rotation_angle, axisID);
		}

	/* Check is it necessary to rotate the membrane: */
	if (curr_mol_complexSP->move_bits & MEMBRANE_MASK)
		{
		RotateMembrane_ (curr_mol_complexSP, configSP,
				 rotation_angle, axisID);
		}

	/* Check is it necessary to rotate the structure at all: */
	if (!(curr_mol_complexSP->move_bits & STRUCTURE_MASK)) continue;

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

	/* Number of atoms in a macromolecular complex: */
	atomsN = curr_mol_complexSP->atomsN;

	/* Scan all atoms in the current complex: */
	for (atomI = 0; atomI < atomsN; atomI++)
		{
		/* Pointer to the current atom: */
		curr_atomSP = curr_mol_complexSP->atomSP + atomI;

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

		/* Rotate these coordinates: */
		switch (axisID)
			{
			/* Rotate around x: */
			case 1:
				x_new =  x;
				y_new =  y * cos_angle - z * sin_angle;
				z_new =  y * sin_angle + z * cos_angle;
				break;

			/* Rotate around y: */
			case 2:
				x_new =  x * cos_angle + z * sin_angle;
				y_new =  y;
				z_new = -x * sin_angle + z * cos_angle;
				break;

			/* Rotate around z: */
			case 3:
				x_new =  x * cos_angle - y * sin_angle;
				y_new =  x * sin_angle + y * cos_angle;
				z_new =  z;
				break;

			/* The impossible case: */
			default:
				;
				break;

			}

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

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

	/* Set the position_changedF: */
	curr_mol_complexSP->position_changedF = 1;
	}

/* Return edit mode index: */
return edit_modeI;
}

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


