/* ImLib3D
 * Copyright (c) 2001, ULP-IPB Strasbourg.
 *
 * 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.
 */
/*! \file Draw.hxx */

#ifndef _Draw_hxx
#define _Draw_hxx

//! Draws a line using Bresenhams algorithm. The floating point vectors for start and end are made integer vectors (rounding to nearest start stop voxel)
template <class ImageType>
void
IP3D::DrawSegment3D(const ImageType& src , const Segment3D& segment , ImageType& res0, DrawingContext<typename ImageType::value_type>* _dc)
{
	ImageProcessorArgs<ImageType> args(ImArgs(src),ImageArg<ImageType>(res0, IMARG_INPLACE));
	args.SameSize();
	ImageType &res=args.GetDest();

	if (!_dc)
		_dc = new DrawingContext<typename ImageType::value_type>; // standard 
	DrawingContext<typename ImageType::value_type> & dc = *_dc;

	RectZone3Di cube(0,0,0,res.Width(), res.Height(), res.Depth());
	Vect3Di pos = floor(segment.P0);
	int dx = (int)segment.P1.x - (int)segment.P0.x;
	int dy = (int)segment.P1.y - (int)segment.P0.y;
	int dz = (int)segment.P1.z - (int)segment.P0.z;
	int xInc = (dx < 0) ? -1 : 1;
	int l = abs(dx);
	int yInc = (dy < 0) ? -1 : 1;
	int m = abs(dy);
	int zInc = (dz < 0) ? -1 : 1;
	int n = abs(dz);
	int dx2 = l << 1;
	int dy2 = m << 1;
	int dz2 = n << 1;
	
	int err1, err2;
	if ((l >=m) && (l >=n))
	{
		err1 = dy2 - l;
		err2 = dz2 - l;
		for (int i=0; i< l; i++)
		{
			if (cube.IsInside(pos))
				res(pos) = dc.GetPen().Color();
			if (err1 > 0)
			{
				pos.y += yInc;
				err1 -= dx2;
			}
			if (err2 > 0)
			{
				pos.z += zInc;
				err2 -= dx2;
			}
			err1 += dy2;
			err2 += dz2;
			pos.x += xInc;
		}
	}
	else if ((m >= l) && (m >= n))
	{
		err1 = dx2 - m;
		err2 = dz2 - m;
		for (int i=0; i < m; i++)
		{
			if (cube.IsInside(pos))
				res(pos) = dc.GetPen().Color();
			if (err1 > 0)
			{
				pos.x += xInc;
				err1 -= dy2;
			}
			if (err2 > 0)
			{
				pos.z += zInc;
				err2 -= dy2;
			}
			err1 += dx2;
			err2 += dz2;
			pos.y += yInc;
		}
	}
	else 
	{
		err1 = dy2 - n;
		err2 = dx2 - n;
		for (int i=0; i < n; i++)
		{
			if (cube.IsInside(pos))
				res(pos) = dc.GetPen().Color();
			if (err1 > 0)
			{
				pos.y += yInc;
				err1 -= dz2;
			}
			if (err2 > 0)
			{
				pos.x += xInc;
				err2 -= dz2;
			}
			err1 += dy2;
			err2 += dx2;
			pos.z += zInc;
		}
	}
	if (cube.IsInside(pos))
		res(pos) = dc.GetPen().Color();
}
#endif// _Draw_hxx
