/*************************************************************************
Copyright(c) 2004-2006 Analog Devices, Inc. All Rights Reserved.

This software is proprietary and confidential.  By using this software you agree
to the terms of the associated Analog Devices License Agreement.

File Name: adi_graph_line.c 

Description - Part of 'adi_graph' library.  See readme file for details.

*********************************************************************************/

#include <stdlib.h>
#include "adi_graph.h"
#include "adi_graph_driver.h"

#define INCREMENT_BY_INT (1<<16)
#define INCREMENT_BY_XSLOPE ((int) ( ( (float) ( gp->x2 - gp->x1 ) / ( gp->y2 - gp->y1 ) ) * (float) (1<<16) ))
#define INCREMENT_BY_YSLOPE ((int) ( ( (float) ( gp->y2 - gp->y1 ) / ( gp->x2 - gp->x1 ) ) * (float) (1<<16) ))

#define PLOT_POINT()																											\
		{																														\
			if ( line_on )																										\
			{																													\
				int rounded_x = ( x + (1<<15) ) >> 16;																			\
				int rounded_y = ( y + (1<<15) ) >> 16;																			\
				if ( ! ( rounded_x < clip_x1 || rounded_x > clip_x2 || rounded_y < clip_y1 || rounded_y > clip_y2 ) )			\
					ADI_GRAPH_DRV_WriteToCanvas( rounded_x, rounded_y, gp->fg_color, gp->canvas );								\
			}																													\
																																\
			if ( dash_seed && gp->dash_pattern[ 0 ] && ++dash_count >= gp->dash_pattern[ dash_index ] )							\
			{																													\
				dash_count = 0;																									\
				++dash_index;																									\
				dash_index %= sizeof ( dash_index ) / sizeof ( unsigned char );													\
				line_on = !line_on;																								\
			}																													\
																																\
			if ( dash_seed ) { ++(*dash_seed);	}																				\
		}


void ADI_Line ( ADI_sGraphPrim *gp, int *dash_seed )
{
	/* Scaling up by 2^16 to allow us to keep the slope acculmulation
	   in integer arithmetic. */
	   
	int x, y;   
	int x1 = (gp->x1) << 16;
	int x2 = (gp->x2) << 16;
	int y1 = (gp->y1) << 16;
	int y2 = (gp->y2) << 16;

	int clip_x1 = (gp->clip_x1);// << 16;
	int clip_x2 = (gp->clip_x2);// << 16;
	int clip_y1 = (gp->clip_y1);// << 16;
	int clip_y2 = (gp->clip_y2);// << 16;
	
	ADI_SwapIfGreaterThan ( clip_x1, clip_x2 );
	ADI_SwapIfGreaterThan ( clip_y1, clip_y2 );
	
	int delta, incr_x, incr_y;
	int dx = abs ( x2 - x1 );
	int dy = abs ( y2 - y1 );
	
	int dash_index = 0;
	int dash_count = 0;
	bool line_on = true;
	
	if ( dash_seed && gp->dash_pattern[ 0 ] )
	{
		int i;
		
		for ( i = 0; i < *dash_seed; ++i )
		{
			if ( ++dash_count >= gp->dash_pattern[ dash_index ] )
			{
				dash_count = 0;																									\
				++dash_index;																									\
				dash_index %= sizeof ( dash_index ) / sizeof ( unsigned char );													\
				line_on = !line_on;																								\
			}
		}
	}

	if ( x2 >= x1 ) /* +x */
	{
		if ( y2 >= y1 ) /* +x,+y */
		{
			if ( dx >= dy )
			{
				/* NEE */
				incr_x = INCREMENT_BY_INT;
				incr_y = INCREMENT_BY_YSLOPE;
			}
			else 
			{
				/* NEN */
				incr_x = INCREMENT_BY_XSLOPE;
				incr_y = INCREMENT_BY_INT;
			}
		}
		else /* +x,-y */
		{
			if ( dx >= dy )
			{
				/* SEE */
				incr_x = INCREMENT_BY_INT;
				incr_y = INCREMENT_BY_YSLOPE;
			}
			else 
			{
				/* SES */
				incr_x = -1*INCREMENT_BY_XSLOPE;
				incr_y = -1*INCREMENT_BY_INT;
			}
		}
	}
	else /* -x */
	{
		if ( y2 >= y1 ) /* -x,+y */
		{
			if ( dx >= dy )
			{
				/* NWW */
				incr_x = -1*INCREMENT_BY_INT;
				incr_y = -1*INCREMENT_BY_YSLOPE;
			}
			else 
			{
				/* NWN */
				incr_x = INCREMENT_BY_XSLOPE;
				incr_y = INCREMENT_BY_INT;
			}
		}
		else /* -x,-y */
		{
			if ( dx >= dy )
			{
				/* SWW */
				incr_x = -1*INCREMENT_BY_INT;
				incr_y = -1*INCREMENT_BY_YSLOPE;
			}
			else 
			{
				/* SWS */
				incr_x = -1*INCREMENT_BY_XSLOPE;
				incr_y = -1*INCREMENT_BY_INT;
			}
		}
	}
	
 	x = x1;
 	y = y1;
 	

	if ( x2 > x1 )
	{
		
		for ( ; x <= x2; x += incr_x, y += incr_y )
		{
			PLOT_POINT();
		}
	}
	else if ( x2 < x1 )
	{
		for ( ; x >= x2; x += incr_x, y += incr_y )
		{
			PLOT_POINT();
		}
	}
	else if ( y1 <= y2 )
	{
		for ( ; y <= y2; y += INCREMENT_BY_INT )
		{
			PLOT_POINT();
		}
	}
	else
	{
		for ( ; y >= y2; y -= INCREMENT_BY_INT )
		{
			PLOT_POINT();
		}
	}
}

void ADI_Point ( ADI_sGraphPrim *gp )
{
	int x = gp->x1;
	int y = gp->y1;

	int clip_x1 = gp->clip_x1;
	int clip_x2 = gp->clip_x2;
	int clip_y1 = gp->clip_y1;
	int clip_y2 = gp->clip_y2;
	
	ADI_SwapIfGreaterThan ( clip_x1, clip_x2 );
	ADI_SwapIfGreaterThan ( clip_y1, clip_y2 );
	
	/* Clip the point */
	if ( clip_x1 > x || clip_x2 < x || clip_y1 > y || clip_y2 < y )
		return;
	
	ADI_GRAPH_DRV_WriteToCanvas( x, y, gp->fg_color, gp->canvas );
}

void ADI_PointConsideringFillPattern ( ADI_sGraphPrim *gp )
{
	if ( ADI_FILL_SOLID == gp->fill_style )
	{
		ADI_Point ( gp );
	}
	else
	{
		if ( gp->fill_pattern [ gp->y1 % 8 ] & ( 1 << ( gp->x1 % 8 ) ) )
		{
			ADI_Point ( gp );
		}
		else if ( ADI_FILL_OPAQUE == gp->fill_style )
		{
			int fg_color_cache = gp->fg_color;
			gp->fg_color = gp->bg_color;
			ADI_Point ( gp );
			gp->fg_color = fg_color_cache;
		}
				
	}
}


