/* This file is part of `mogul'
 * Copyright (c) 1995 David Deaven
 *
 * In the following, "you" and "You" refer to any entity other than 
 * the Microsoft corporation and its subsidiaries.  All rights are
 * reserved by the author in connection with Microsoft and its
 * subsidiaries.  The Microsoft Network is explicity denied the right
 * to copy, reproduce, or transmit this software.
 *
 *   This program 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.
 *
 *   This program 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 this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Author: deaven $
 * $Id: geometry.c,v 2.0 1996/07/09 20:59:55 deaven Exp deaven $
 */

#include <stdio.h>
#include <math.h>
#include "dmd/memory.h"
#include "dmd/dmdtimer.h"
#include "dmd/element.h"
#include "mogul.h"

/* Local procedure to draw a line in box units.
 */
static int bline(MogulPlot p, struct commonAttributes *attr, 
  double x, double y, double z,
  double q, double r, double s, double *b)
{
	double p0[3], p1[3];

	p0[0] = x * b[0] + y * b[1] + z * b[2];
	p0[1] = x * b[3] + y * b[4] + z * b[5];
	p0[2] = x * b[6] + y * b[7] + z * b[8];
	p1[0] = q * b[0] + r * b[1] + s * b[2];
	p1[1] = q * b[3] + r * b[4] + s * b[5];
	p1[2] = q * b[6] + r * b[7] + s * b[8];
	return MogulAddSegmentedLine(p, attr, p0, p1);
}

int MogulAddBox(MogulPlot p, struct commonAttributes *attr, Geometry *g)
{
	if(
	  bline(p, attr, -.5, -.5, -.5, +.5, -.5, -.5, g->box) ||
	  bline(p, attr, -.5, -.5, -.5, -.5, +.5, -.5, g->box) ||
	  bline(p, attr, -.5, -.5, -.5, -.5, -.5, +.5, g->box) ||
	  bline(p, attr, +.5, -.5, -.5, +.5, +.5, -.5, g->box) ||
	  bline(p, attr, +.5, -.5, -.5, +.5, -.5, +.5, g->box) ||
	  bline(p, attr, -.5, +.5, -.5, +.5, +.5, -.5, g->box) ||
	  bline(p, attr, -.5, +.5, -.5, -.5, +.5, +.5, g->box) ||
	  bline(p, attr, -.5, -.5, +.5, +.5, -.5, +.5, g->box) ||
	  bline(p, attr, -.5, -.5, +.5, -.5, +.5, +.5, g->box) ||
	  bline(p, attr, +.5, +.5, +.5, -.5, +.5, +.5, g->box) ||
	  bline(p, attr, +.5, +.5, +.5, +.5, -.5, +.5, g->box) ||
	  bline(p, attr, +.5, +.5, +.5, +.5, +.5, -.5, g->box)
	) return -1;
	return 0;
}

/* Add all of the atoms in the geometry, and default bonds,
 * and a bounding box if the structure is periodic.  Finally,
 * a text label with the geometry name.
 */
int MogulSimpleAddGeometry(MogulPlot p, Geometry *g)
{
	int i, j, status = 0;
	Element *e;
	double x[3], x1[3], x2[3], c[3], bondr2, bondRadius, *radius, *bondr;
	struct commonAttributes attr;

	if(g == NULL || g->n < 1) return 0;

	radius = dmalloc(g->n);
	bondr = dmalloc(g->n);

	/* all of the atoms */
	MogulDefaultAttributes(p, &attr);
	for(i=0; i<3; i++) c[i] = 0.0;
	for(i=0; i<g->n; i++) {
	  e = GuessElement(g->aname[i]);
	  radius[i] = e->radius;
	  bondr[i] = e->bondr;
	  attr.color = ColorFromName(e->color);
	  mmul3(g->box, g->x + 3*i, x);
	  status = MogulAddAtom(p, &attr, x, radius[i]);
	  if(status < 0) goto bailout;
	  for(j=0; j<3; j++) c[j] += x[j];
	}
	for(i=0; i<3; i++) c[i] /= g->n;

	{ double rmin = radius[0], rmax = radius[0];
	  for(i=1; i<g->n; i++) {
	    if(radius[i] < rmin) rmin = radius[i];
	    if(radius[i] > rmax) rmax = radius[i];
	  }
	  rmin *= 0.9;
	  bondRadius = 0.25 * (rmin + rmax);
	  if(rmin > 0.0 && bondRadius > rmin) bondRadius = rmin;
	}

	/* all of the bonds */
	MogulDefaultAttributes(p, &attr);
	attr.color = ColorFromName("slate");
	attr.mode &= ~MA_MINIMAL;
	for(i=0; i<(g->n - 1); i++)
	{
	  if(bondr[i] > 0.0)
	  {
	    mmul3(g->box, g->x + 3*i, x1);
	    for(j=i+1; j<g->n; j++)
	    {
	      if(bondr[j] > 0.0)
	      {
		bondr2 = bondr[i] + bondr[j];
		bondr2 *= bondr2;
		if(Gdisp2(g, i, j, x) < bondr2)
		{
		  mmul3(g->box, g->x + 3*j, x2);
		  status = MogulAddBondBetween(p, &attr,
		    x1, radius[i], x2, radius[j], bondRadius);
		  if(status < 0) goto bailout;
		}
	      }
	    }
	  }
	}

	if(g->period & GP_PERIODIC) {
	  MogulDefaultAttributes(p, &attr);
	  attr.color = p->attr.foreground;
	  status = MogulAddBox(p, &attr, g);
	  if(status < 0) goto bailout;
	}

	if(g->name && strlen(g->name) > 0)
	{
	  double height = 1.0;
	  MogulDefaultAttributes(p, &attr);
	  attr.color = p->attr.foreground;
	  status = MogulAddLabel(p, &attr, c, g->name, height);
	  if(status < 0) goto bailout;
	}

bailout:
	myfree(radius);
	myfree(bondr);

	return (status >= 0 ? 0 : -1);
}

int MogulAddGeometry(MogulPlot p, MogulRules r, Geometry *g, NeighborList *nl)
{
	int i, j, jnl, status = 0;
	double x[3], x1[3], x2[3], c[3], *radius;
	struct ruleAttributes *rule;

	if(g == NULL || g->n < 1) return 0;

	radius = dmalloc(g->n);

	/* all of the atoms */
	for(i=0; i<3; i++) c[i] = 0.0;
	for(i=0; i<g->n; i++) {
	  rule = MogulGetAtomRule(r, g->aname[i]);
	  mmul3(g->box, g->x + 3*i, x);
	  status = MogulAddAtom(p, &(rule->attr), x, rule->u.atom.radius);
	  if(status < 0) goto bailout;
	  for(j=0; j<3; j++) c[j] += x[j];
	  radius[i] = rule->u.atom.radius;
	}
	for(i=0; i<3; i++) c[i] /= g->n;

	/* all of the bonds */
	for(i=0; i<(g->n - 1); i++)
	{
	  mmul3(g->box, g->x + 3*i, x1);
	  for(jnl=nl->mrkr[i]; jnl<(nl->mrkr[i+1]); jnl++)
	  {
	    j = getnlindex(nl->list[jnl]);
	    rule = MogulGetBondRule(r, g->aname[i], g->aname[j]);
	    if(Gdisp2(g, i, j, x) < rule->u.bond.bondr * rule->u.bond.bondr)
	    {
	      mmul3(g->box, g->x + 3*j, x2);
	      status = MogulAddBondBetween(p, &(rule->attr),
		x1, radius[i], x2, radius[j], rule->u.bond.radius);
	      if(status < 0) goto bailout;
	    }
	  }
	}

	if(g->period & GP_PERIODIC) {
	  struct commonAttributes attr;
	  MogulDefaultAttributes(p, &attr);
	  attr.color = p->attr.foreground;
	  status = MogulAddBox(p, &attr, g);
	  if(status < 0) goto bailout;
	}

	if(g->name && strlen(g->name) > 0)
	{
	  double height = 1.0;
	  struct commonAttributes attr;
	  MogulDefaultAttributes(p, &attr);
	  attr.color = p->attr.foreground;
	  status = MogulAddLabel(p, &attr, c, g->name, height);
	  if(status < 0) goto bailout;
	}

bailout:
	myfree(radius);

	return (status >= 0 ? 0 : -1);
}

