Table of Contents

StellarisWare Graphics Library Polygon

polygon.c

//*****************************************************************************
//
// polygon.c - Routines for drawing and filling polygons.
//             Addition to StellarisWare library.
//
// Copyright (c) 2010 Mikk Leini, TUT Robotics Club NPO
//
// Polygon fill algorithm is based on Darel Rex Finley suggestions:
//   http://www.alienryderflex.com/polygon_fill
//
//*****************************************************************************
 
#include "driverlib/debug.h"
#include "grlib/grlib.h"
 
//*****************************************************************************
//
//! \addtogroup primitives_api
//! @{
//
//*****************************************************************************
 
//*****************************************************************************
//
//! Draws a polygon.
//!
//! \param pContext is a pointer to the drawing context to use.
//! \param pRect is a pointer to the array of structures containing the points
//! of the polygon.
//! \param ulNumPoints is a number of points in array.
//! \param bClosedLoop indicates wheter to draw line between the first and
//! the last point.
//!
//! This function draws lines between the consecutive points and if requiered
//! the line between the first and the last point.
//!
//! \return None.
//
//*****************************************************************************
void
GrPolyDraw(const tContext *pContext, const tPoint *pPoints, 
           unsigned long ulNumPoints, unsigned char ucClosedLoop)
{
	unsigned long ulIdx;
 
    //
    // Check the arguments.
    //
    ASSERT(pContext);
    ASSERT(pPoints);
	ASSERT(sNumPoints > (bClosedLoop ? 2 : 1));
 
	//
	// Draw the lines between points
	//
	for(ulIdx = 0; ulIdx < ulNumPoints - 1; ulIdx++)
	{
		GrLineDraw(pContext, pPoints[ulIdx].sX, pPoints[ulIdx].sY,
		           pPoints[ulIdx + 1].sX, pPoints[ulIdx + 1].sY);
	}
 
	//
	// Draw a closing line between first and last point?
	//
	if(ucClosedLoop)
	{
		GrLineDraw(pContext, pPoints[0].sX, pPoints[0].sY,
		           pPoints[ulNumPoints - 1].sX, pPoints[ulNumPoints - 1].sY);
	}
}
 
//*****************************************************************************
//
//! Draws a filled polygon.
//!
//! \param pContext is a pointer to the drawing context to use.
//! \param pRect is a pointer to the array of structures containing the points
//! of the polygon.
//! \param ulNumPoints is a number of points in array.
//!
//! This function fills a polygon specified by the consecutive points.
//!
//! The clipping of the polygon to the clipping rectangle is performed within
//! this routine;
//!
//! \return None.
//
//*****************************************************************************
void
GrPolyFill(const tContext *pContext, const tPoint *pPoints, 
           unsigned long ulNumPoints)
{    
	unsigned long ulIdx, ulIdx2;
	unsigned long ulNumNodes;
	short sY;
	short pNodeList[100];	
	short sSwap;
 
    //
    // Check the arguments.
    //
    ASSERT(pContext);
    ASSERT(pPoints);
 	ASSERT(sNumPoints > 2);
 
	//
	// Loop through the rows of the clipping area.
	//
	for(sY = pContext->sClipRegion.sYMin; sY <= pContext->sClipRegion.sYMax; sY++)
	{
		//
		// Build a list of nodes.
		//
  		ulNumNodes = 0;
 
		//
		// Search for X coordinates of polygons on the current row.
		//
		for(ulIdx = 0; ulIdx < ulNumPoints; ulIdx++)
		{
			//
			// Get the index of next point and wrap around points count.
			//
			ulIdx2 = (ulIdx + 1) % ulNumPoints;
 
			//
			// Check if the polygon line exists on the same row.
			//
			if(((pPoints[ulIdx].sY <  sY) && (pPoints[ulIdx2].sY >= sY)) ||
               ((pPoints[ulIdx].sY >= sY) && (pPoints[ulIdx2].sY <  sY)))
			{
				//
				// Make sure nodes list isn't overflowed.
				//
				if(ulNumNodes >= 100) break;
 
				//
				// Calculate the intersection point X coordinate
				// of the polygon edge.
				//
				pNodeList[ulNumNodes++] =
					(pPoints[ulIdx].sX + (sY - pPoints[ulIdx].sY) *
					(pPoints[ulIdx2].sX - pPoints[ulIdx].sX) /
					(pPoints[ulIdx2].sY - pPoints[ulIdx].sY));
			}
		}
 
		//
		// Sort the nodes, via a simple “Bubble” sort.
		//
		ulIdx = 0;
 
		while(ulIdx + 1 < ulNumNodes)
		{
			if(pNodeList[ulIdx] > pNodeList[ulIdx + 1])
			{
				sSwap = pNodeList[ulIdx];
				pNodeList[ulIdx] = pNodeList[ulIdx + 1];
				pNodeList[ulIdx + 1] = sSwap;
 
				if(ulIdx)
				{
					ulIdx--;
				}
			}
			else
			{
				ulIdx++;
			}
		}
 
		//
		// Fill the pixels between node pairs.
		//
		for(ulIdx = 0; ulIdx < ulNumNodes; ulIdx += 2)
		{
			//
			// Break when lines go out of clipping region.
			//
			if(pNodeList[ulIdx] > pContext->sClipRegion.sXMax) break;
 
			//
			// Skip when line ends before clipping region.
			//
			if(pNodeList[ulIdx + 1] < pContext->sClipRegion.sXMin) continue;
 
			//
			// Clip the line from left.
			//
			if(pNodeList[ulIdx] < pContext->sClipRegion.sXMin)
			{
				pNodeList[ulIdx] = pContext->sClipRegion.sXMin;
			}
 
			//
			// Clip the line from right.
			//
			if(pNodeList[ulIdx + 1] > pContext->sClipRegion.sXMax)
			{
				pNodeList[ulIdx + 1] = pContext->sClipRegion.sXMax;
			}
 
			//
			// Call the low level horizontal line drawing routine.
			//
			DpyLineDrawH(pContext->pDisplay, pNodeList[ulIdx],
			             pNodeList[ulIdx + 1], sY, pContext->ulForeground);
		}		
	}
}
 
//*****************************************************************************
//
//! Draws a triangle.
//!
//! \param pContext is a pointer to the drawing context to use.
//! \param pPoint1 is a pointer to the first point.
//! \param pPoint2 is a pointer to the second point.
//! \param pPoint3 is a pointer to the third point.
//!
//! This function draws a triangle specified by three points.
//!
//! \return None.
//
//*****************************************************************************
void
GrTriangleDraw(const tContext *pContext, const tPoint *pPoint1, 
               const tPoint *pPoint2, const tPoint *pPoint3)
{
	tPoint pPoints[3];
 
	pPoints[0] = *pPoint1;
	pPoints[1] = *pPoint2;
	pPoints[2] = *pPoint3;
 
	GrPolyDraw(pContext, pPoints, 3, 1);
}
 
//*****************************************************************************
//
//! Draws a filled triangle.
//!
//! \param pContext is a pointer to the drawing context to use.
//! \param pPoint1 is a pointer to the first point.
//! \param pPoint2 is a pointer to the second point.
//! \param pPoint3 is a pointer to the third point.
//!
//! This function fills a triangle specified by three points.
//!
//! \return None.
//
//*****************************************************************************
void
GrTriangleFill(const tContext *pContext, const tPoint *pPoint1, 
               const tPoint *pPoint2, const tPoint *pPoint3)
{
	tPoint pPoints[3];
 
	pPoints[0] = *pPoint1;
	pPoints[1] = *pPoint2;
	pPoints[2] = *pPoint3;
 
	GrPolyFill(pContext, pPoints, 3);
}
 
//*****************************************************************************
//
//! Draws a quad.
//!
//! \param pContext is a pointer to the drawing context to use.
//! \param pPoint1 is a pointer to the first point.
//! \param pPoint2 is a pointer to the second point.
//! \param pPoint3 is a pointer to the third point.
//! \param pPoint4 is a pointer to the fourth point.
//!
//! This function draws a quad specified by four points.
//!
//! \return None.
//
//*****************************************************************************
void
GrQuadDraw(const tContext *pContext, const tPoint *pPoint1, 
           const tPoint *pPoint2, const tPoint *pPoint3,
		   const tPoint *pPoint4)
{
	tPoint pPoints[4];
 
	pPoints[0] = *pPoint1;
	pPoints[1] = *pPoint2;
	pPoints[2] = *pPoint3;
	pPoints[3] = *pPoint4;
 
	GrPolyDraw(pContext, pPoints, 4, 1);
}
 
//*****************************************************************************
//
//! Draws a filled quad.
//!
//! \param pContext is a pointer to the drawing context to use.
//! \param pPoint1 is a pointer to the first point.
//! \param pPoint2 is a pointer to the second point.
//! \param pPoint3 is a pointer to the third point.
//! \param pPoint4 is a pointer to the fourth point.
//!
//! This function fills a quad specified by four points.
//!
//! The clipping of the polygon to the clipping rectangle is performed within
//! this routine;
//!
//! \return None.
//
//*****************************************************************************
void
GrQuadFill(const tContext *pContext, const tPoint *pPoint1, 
           const tPoint *pPoint2, const tPoint *pPoint3,
		   const tPoint *pPoint4)
{
	tPoint pPoints[4];
 
	pPoints[0] = *pPoint1;
	pPoints[1] = *pPoint2;
	pPoints[2] = *pPoint3;
	pPoints[3] = *pPoint4;
 
	GrPolyFill(pContext, pPoints, 4);
}
 
//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************

grlib.h

//*****************************************************************************
//
//! This structure defines the point.
//
//*****************************************************************************
typedef struct
{
    //
    //! The X coordinate of the point.
    //
    short sX;
 
    //
    //! The Y coordinate of the point.
    //
    short sY;
}
tPoint;
 
// Add these also:
 
extern void GrPolyDraw(const tContext *pContext, const tPoint *pPoints, 
           unsigned long ulNumPoints, unsigned char ucClosedLoop);
extern void GrPolyFill(const tContext *pContext, const tPoint *pPoints, 
           unsigned long ulNumPoints);
extern void GrTriangleDraw(const tContext *pContext, const tPoint *pPoint1, 
               const tPoint *pPoint2, const tPoint *pPoint3);
extern void GrTriangleFill(const tContext *pContext, const tPoint *pPoint1, 
               const tPoint *pPoint2, const tPoint *pPoint3);
extern void GrQuadDraw(const tContext *pContext, const tPoint *pPoint1, 
           const tPoint *pPoint2, const tPoint *pPoint3,
		   const tPoint *pPoint4);
extern void GrQuadFill(const tContext *pContext, const tPoint *pPoint1, 
           const tPoint *pPoint2, const tPoint *pPoint3,
		   const tPoint *pPoint4);