예제 #1
0
파일: ocpndc.cpp 프로젝트: JesperWe/OpenCPN
void ocpnDC::DrawLines( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, bool b_hiqual )
{
    if( dc ) dc->DrawLines( n, points, xoffset, yoffset );
    else if( ConfigurePen() ) {

        glPushAttrib( GL_COLOR_BUFFER_BIT | GL_LINE_BIT | GL_HINT_BIT );      //Save state
        SetGLAttrs( b_hiqual );

        bool b_draw_thick = false;

        SetGLStipple();

        //      Enable anti-aliased lines, at best quality
        if( b_hiqual ) {
            if( m_pen.GetWidth() > 1 ) {
                GLint parms[2];
                glGetIntegerv( GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0] );
                if( m_pen.GetWidth() > parms[1] ) b_draw_thick = true;
                else
                    glLineWidth( wxMax(g_GLMinLineWidth, m_pen.GetWidth()) );
            } else
                glLineWidth( wxMax(g_GLMinLineWidth, 1) );
        } else {
            if( m_pen.GetWidth() > 1 ) {
                GLint parms[2];
                glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
                if( m_pen.GetWidth() > parms[1] ) b_draw_thick = true;
                else
                    glLineWidth( wxMax(g_GLMinLineWidth, m_pen.GetWidth()) );
            } else
                glLineWidth( wxMax(g_GLMinLineWidth, 1) );
        }

        if( b_draw_thick/*m_pen.GetWidth() > 1*/) {
            wxPoint p0 = points[0];
            for( int i = 1; i < n; i++ ) {
                DrawThickLine( p0.x + xoffset, p0.y + yoffset, points[i].x + xoffset,
                        points[i].y + yoffset, m_pen, b_hiqual );
                p0 = points[i];
            }
        } else {
            glBegin( GL_LINE_STRIP );
            for( int i = 0; i < n; i++ )
                glVertex2i( points[i].x + xoffset, points[i].y + yoffset );
            glEnd();
        }
        glPopAttrib();            // restore state
    }
}
예제 #2
0
파일: ocpndc.cpp 프로젝트: JesperWe/OpenCPN
void ocpnDC::DrawLine( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, bool b_hiqual )
{
    if( dc ) dc->DrawLine( x1, y1, x2, y2 );
    else {
        glPushAttrib( GL_COLOR_BUFFER_BIT | GL_LINE_BIT | GL_ENABLE_BIT | GL_HINT_BIT ); //Save state

        if( ConfigurePen() ) {

            glDisable( GL_MULTISAMPLE );

            bool b_draw_thick = false;

            float pen_width = wxMax(g_GLMinLineWidth, m_pen.GetWidth());

            //      Enable anti-aliased lines, at best quality
            if( b_hiqual ) {
                SetGLStipple();

                glEnable( GL_LINE_SMOOTH );
                glEnable( GL_BLEND );
                glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
                glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );

                if( pen_width > 1.0 ) {
                    GLint parms[2];
                    glGetIntegerv( GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0] );
                    if( pen_width > parms[1] ) b_draw_thick = true;
                    else
                        glLineWidth( pen_width );
                } else
                    glLineWidth( pen_width );
            } else {
                glDisable( GL_LINE_SMOOTH );
                glDisable( GL_BLEND );

                if( pen_width > 1 ) {
                    GLint parms[2];
                    glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
                    if( pen_width > parms[1] ) b_draw_thick = true;
                    else
                        glLineWidth( pen_width );
                } else
                    glLineWidth( pen_width );
            }

            if( b_draw_thick ) DrawThickLine( x1, y1, x2, y2, m_pen, b_hiqual );
            else {
                wxDash *dashes;
                int n_dashes = m_pen.GetDashes( &dashes );
                if( n_dashes ) {
                    double angle = atan2( (double) ( y2 - y1 ), (double) ( x2 - x1 ) );
                    double cosa = cos( angle );
                    double sina = sin( angle );
                    double t1 = m_pen.GetWidth();

                    double lpix = sqrt(
                            (double) ( x1 - x2 ) * ( x1 - x2 )
                                    + (double) ( y1 - y2 ) * ( y1 - y2 ) );
                    double lrun = 0.;
                    double xa = x1;
                    double ya = y1;
                    double ldraw = t1 * dashes[0];
                    double lspace = t1 * dashes[1];

                    while( lrun < lpix ) {
                        //    Dash
                        double xb = xa + ldraw * cosa;
                        double yb = ya + ldraw * sina;

                        if( ( lrun + ldraw ) >= lpix )         // last segment is partial draw
                                {
                            xb = x2;
                            yb = y2;
                        }

                        glBegin( GL_LINES );
                        glVertex2f( xa, ya );
                        glVertex2f( xb, yb );
                        glEnd();

                        xa = xa + ( lspace + ldraw ) * cosa;
                        ya = ya + ( lspace + ldraw ) * sina;
                        lrun += lspace + ldraw;

                    }
                } else                    // not dashed
                {
                    glBegin( GL_LINES );
                    glVertex2i( x1, y1 );
                    glVertex2i( x2, y2 );
                    glEnd();
                }
            }
        }

        glPopAttrib();
    }
}
예제 #3
0
// Draw a string in the specified font
void BaseEngine::DrawString(int iX, int iY, const char* pText, 
							unsigned int uiColour, Font* pFont, SDL_Surface* pTarget )
{
	if ( pTarget == NULL )
		pTarget = m_pActualScreen;

	if ( pFont == NULL )
		pFont = g_pMainFont;

	if ( m_bInsideDraw )
		if (SDL_MUSTLOCK(m_pActualScreen)) 
			SDL_UnlockSurface(m_pActualScreen);

	SDL_Color color = { (uiColour&0xff0000)>>16, (uiColour&0xff00)>>8, (uiColour&0xff), 0 };

	if ( ( pFont != NULL ) && ( pFont->GetFont() != NULL ) )
	{
		SDL_Surface *sText = TTF_RenderText_Solid( pFont->GetFont(), pText, color );
		SDL_Rect rcDest = {iX,iY,0,0};

		SDL_BlitSurface( sText, NULL, pTarget, &rcDest );
		SDL_FreeSurface( sText );
	}

	if ( m_bInsideDraw )
		if (SDL_MUSTLOCK(m_pActualScreen))
			SDL_LockSurface(m_pActualScreen);
}


// Draw a triangle, as two vertical sided regions.
void BaseEngine::DrawTriangle(
		double fX1, double fY1,
		double fX2, double fY2,
		double fX3, double fY3, 
		unsigned int uiColour, 
		SDL_Surface* pTarget )
{
	if ( pTarget == NULL )
		pTarget = m_pActualScreen;

	// Ensure order is 1 2 3 from left to right
	if ( fX1 > fX2 ) { Swap( fX1,fX2 ); Swap( fY1,fY2 ); } // Bigger of 1 and 2 is in position 2
	if ( fX2 > fX3 ) { Swap( fX2,fX3 ); Swap( fY2,fY3 ); } // Bigger of new 2 and 3 is in position 3
	if ( fX1 > fX2 ) { Swap( fX1,fX2 ); Swap( fY1,fY2 ); } // Bigger of 1 and new 2 is in position 2

	if ( fX1 == fX2 )
		DrawVerticalSidedRegion( fX1, fX3, fY1, fY3, fY2, fY3, uiColour, pTarget );
	else if ( fX2 == fX3 )
		DrawVerticalSidedRegion( fX1, fX3, fY1, fY2, fY1, fY3, uiColour, pTarget );
	else
	{
		// Split into two triangles. Find position on line 1-3 to split at
		double dSplitPointY = (double)fY1 + 
			(   ( (double)((fX2-fX1)*(fY3-fY1)) )
			/ (double)(fX3-fX1)   );
		DrawVerticalSidedRegion( fX1, fX2, fY1, fY2, fY1, dSplitPointY, uiColour, pTarget );
		DrawVerticalSidedRegion( fX2, fX3, fY2, fY3, dSplitPointY, fY3, uiColour, pTarget );
	}
}


// Draw a vertical sided region.
// If two points are the same then it is a triangle.
// To do an arbitrary triangle, just draw two next to each other, one for left and one for right.
void BaseEngine::DrawVerticalSidedRegion(
         double fX1, double fX2,// X positions
         double fY1a, double fY2a, // Start y positions for x1 and x2
         double fY1b, double fY2b, // End y positions for x1 and x2
         unsigned int uiColour,
         SDL_Surface* pTarget)
{
     if ( pTarget == NULL )
         pTarget = m_pActualScreen;

     // Ensure X1<  X2, otherwise steps will go wrong!
     // Switch the points if x and y are wrong way round
     if ( fX2<  fX1 ) { Swap(fX1,fX2); Swap(fY1a,fY2a); Swap(fY1b,fY2b);  }

     int iXStart = (int)(fX1+0.5);
     int iXEnd = (int)(fX2+0.5);

     // If integer x positions are the same then avoid floating point inaccuracy problems by a special case
     if ( iXStart == iXEnd )
     {
         int iYStart = (int)(fY1a+0.5);
         int iYEnd = (int)(fY2a+0.5);
         for ( int iY = iYStart ; iY<= iYEnd ; iY++ )
             SafeSetPixel( iXStart, iY, uiColour, pTarget );
     }
     else
     {
         // Draw left hand side
         int iYStart = (int)(fY1a+0.5);
         int iYEnd = (int)(fY1b+0.5);
         if ( iYStart>  iYEnd ) Swap( iYStart, iYEnd );
         //printf( "Firstline %d to %d (%f to %f)\n", iYStart, iYEnd, fY1a, fY1b );
         for ( int iY = iYStart ; iY<= iYEnd ; iY++ )
             SafeSetPixel( iXStart, iY, uiColour, pTarget );

         // Draw the middle
         for ( int iX = iXStart+1 ; iX<  iXEnd ; iX++ )
         {
             double fYStart = fY1a + ( (((double)iX)-fX1)*(fY2a-fY1a)) /(fX2-fX1);
             double fYEnd = fY1b + ((((double)iX)-fX1)*(fY2b-fY1b))/(fX2-fX1);
             if ( fYEnd<  fYStart ) Swap( fYStart, fYEnd );
             int iYStart = (int)(fYStart+0.5);
             int iYEnd = (int)(fYEnd+0.5);
             //printf( "Line from %d to %d (%f to %f)\n", iYStart, iYEnd, fYStart, fYEnd );
             for ( int iY = iYStart ; iY<= iYEnd ; iY++ )
                 SafeSetPixel( iX, iY, uiColour, pTarget );
         }

         // Draw right hand side
         iYStart = (int)(fY2a+0.5);
         iYEnd = (int)(fY2b+0.5);
         if ( iYStart>  iYEnd ) Swap( iYStart, iYEnd );
         //printf( "Last line %d to %d (%f to %f)\n", iYStart, iYEnd, fY2a, fY2b );
         for ( int iY = iYStart ; iY<= iYEnd ; iY++ )
             SafeSetPixel( iXEnd, iY, uiColour, pTarget );
     }
}



// Draw a rectangle on the specified surface
void BaseEngine::DrawRectangle(int iX1, int iY1, int iX2, int iY2, 
									  unsigned int uiColour, SDL_Surface* pTarget)
{
	if ( pTarget == NULL )
		pTarget = m_pActualScreen;

	if ( iX2 < iX1 ) { int t = iX1; iX1 = iX2; iX2 = t; }
	if ( iY2 < iY1 ) { int t = iY1; iY1 = iY2; iY2 = t; }

	for ( int iX = iX1 ; iX <= iX2 ; iX++ )
		for ( int iY = iY1 ; iY <= iY2 ; iY++ )
			SafeSetPixel( iX, iY, uiColour, pTarget );
}

// Draw an oval on the specified surface
void BaseEngine::DrawOval(int iX1, int iY1, int iX2, int iY2, 
									unsigned int uiColour, SDL_Surface* pTarget)
{
	if ( pTarget == NULL )
		pTarget = m_pActualScreen;

	if ( iX2 < iX1 ) { int t = iX1; iX1 = iX2; iX2 = t; }
	if ( iY2 < iY1 ) { int t = iY1; iY1 = iY2; iY2 = t; }

	double fCentreX = ((double)(iX2+iX1))/2.0;
	double fCentreY = ((double)(iY2+iY1))/2.0;
	double fXFactor = (double)((iX2-iX1) * (iX2-iX1))/4.0;
	double fYFactor = (double)((iY2-iY1) * (iY2-iY1))/4.0;
	double fDist;
	
	for ( int iX = iX1 ; iX <= iX2 ; iX++ )
		for ( int iY = iY1 ; iY <= iY2 ; iY++ )
		{
			fDist = ((double)iX - fCentreX) * ((double)iX - fCentreX)/fXFactor
				+ ((double)iY - fCentreY) * ((double)iY - fCentreY)/fYFactor;
			if ( fDist <= 1.0 )
				SafeSetPixel( iX, iY, uiColour, pTarget );
		}
}

// Draw an oval on the specified surface
void BaseEngine::DrawHollowOval(	int iX1, int iY1, int iX2, int iY2, 
									int iX3, int iY3, int iX4, int iY4, 
									unsigned int uiColour, SDL_Surface* pTarget)
{
	if ( pTarget == NULL )
		pTarget = m_pActualScreen;

	if ( iX2 < iX1 ) Swap( iX1, iX2 );
	if ( iY2 < iY1 ) Swap( iY1, iY2 );
	if ( iX4 < iX3 ) Swap( iX3, iX4 );
	if ( iY4 < iY3 ) Swap( iY3, iY4 );

	double fCentreX1 = ((double)(iX2+iX1))/2.0;
	double fCentreY1 = ((double)(iY2+iY1))/2.0;
	double fXFactor1 = (double)((iX2-iX1) * (iX2-iX1))/4.0;
	double fYFactor1 = (double)((iY2-iY1) * (iY2-iY1))/4.0;
	double fCentreX2 = ((double)(iX4+iX3))/2.0;
	double fCentreY2 = ((double)(iY4+iY3))/2.0;
	double fXFactor2 = (double)((iX4-iX3) * (iX4-iX3))/4.0;
	double fYFactor2 = (double)((iY4-iY3) * (iY4-iY3))/4.0;
	double fDist1, fDist2;
	
	for ( int iX = iX1 ; iX <= iX2 ; iX++ )
		for ( int iY = iY1 ; iY <= iY2 ; iY++ )
		{
			fDist1 = ((double)iX - fCentreX1) * ((double)iX - fCentreX1)/fXFactor1
				+ ((double)iY - fCentreY1) * ((double)iY - fCentreY1)/fYFactor1;
			fDist2 = ((double)iX - fCentreX2) * ((double)iX - fCentreX2)/fXFactor2
				+ ((double)iY - fCentreY2) * ((double)iY - fCentreY2)/fYFactor2;
			if ( ( fDist1 <= 1.0 ) && ( fDist2 >= 1.0 ) )
				SafeSetPixel( iX, iY, uiColour, pTarget );
		}
}


// Draw a line on the specified surface
void BaseEngine::DrawLine(double fX1, double fY1, double fX2, double fY2, 
						unsigned int uiColour, SDL_Surface* pTarget)
{
	if ( pTarget == NULL )
		pTarget = m_pActualScreen;

	int iX1 = (int)(fX1+0.5);
	int iX2 = (int)(fX2+0.5);
	int iY1 = (int)(fY1+0.5);
	int iY2 = (int)(fY2+0.5);

	int iSteps = (iX2-iX1);
	if ( iSteps < 0 ) iSteps = -iSteps;
	if ( iY2 > iY1 ) iSteps += (iY2-iY1); else iSteps += (iY1-iY2);
	iSteps+=2;

	double fXStep = ((double)(fX2-fX1))/iSteps;
	double fYStep = ((double)(fY2-fY1))/iSteps;

	for ( int i = 0 ; i <= iSteps ; i++ )
	{
		SafeSetPixel( (int)(0.5 + fX1 + fXStep*i), (int)(0.5 + fY1 + fYStep*i), uiColour, pTarget );
	}
}



// Draw a thick line on the specified surface
void BaseEngine::DrawThickLine( double fX1, double fY1, double fX2, double fY2, 
							 unsigned int uiColour, int iThickness, SDL_Surface* pTarget)
{
	if ( pTarget == NULL )
		pTarget = m_pActualScreen;

	if ( iThickness < 2 )
	{ // Go to the quicker draw function
		DrawLine(fX1, fY1, fX2, fY2, uiColour, pTarget);
		return;
	}

	double fAngle1 = GetAngle( fX1, fY1, fX2, fY2 );
	double fAngle1a = fAngle1 - ((5 * M_PI) / 4.0);
	double fAngle1b = fAngle1 + ((5 * M_PI) / 4.0);
	double fRectX1 = fX1 + iThickness * cos(fAngle1a) * 0.5;
	double fRectY1 = fY1 + iThickness * sin(fAngle1a) * 0.5;
	double fRectX2 = fX1 + iThickness * cos(fAngle1b) * 0.5;
	double fRectY2 = fY1 + iThickness * sin(fAngle1b) * 0.5;

	double fAngle2 = fAngle1 + M_PI;
	double fAngle2a = fAngle2 - ((5 * M_PI) / 4.0);
	double fAngle2b = fAngle2 + ((5 * M_PI) / 4.0);
	double fRectX3 = fX2 + iThickness * cos(fAngle2a) * 0.5;
	double fRectY3 = fY2 + iThickness * sin(fAngle2a) * 0.5;
	double fRectX4 = fX2 + iThickness * cos(fAngle2b) * 0.5;
	double fRectY4 = fY2 + iThickness * sin(fAngle2b) * 0.5;

	DrawTriangle( fRectX1, fRectY1, fRectX2, fRectY2, fRectX3, fRectY3, uiColour, pTarget );
	DrawTriangle( fRectX3, fRectY3, fRectX4, fRectY4, fRectX1, fRectY1, uiColour, pTarget );
}



// Draw a polygon on the specified surface
void BaseEngine::DrawPolygon( 
		int iPoints, double* pXArray, double* pYArray,
		unsigned int uiColour, SDL_Surface* pTarget)
{
	if ( pTarget == NULL )
		pTarget = m_pActualScreen;

	if ( iPoints == 1 )
	{
		SafeSetPixel( pXArray[0], pYArray[0], uiColour, pTarget );
		return;
	}

	if ( iPoints == 2 )
	{
		DrawLine( pXArray[0], pYArray[0], pXArray[1], pYArray[1], uiColour, pTarget );
		return;
	}

/*	if ( iPoints == 3 )
	{
		printf( "Draw triangle for points 0, 1, 2 of %d available\n", iPoints );
		DrawTriangle( pXArray[0], pYArray[0], pXArray[1], pYArray[1], pXArray[2], pYArray[2],
				uiColour, pTarget );
		return;
	}
*/

	// Otherwise attempt to eliminate a point by filling the polygon, then call this again
	double fXCentre, fYCentre; //fX1, fX2, fX3, fY1, fY2, fY3;
	int i2, i3;
	double fAngle1, fAngle2, fAngle3;

	for ( int i1 = 0 ; i1 < iPoints ; i1++ )
	{
		i2 = i1 + 1; if ( i2 >= iPoints ) i2 -= iPoints;
		i3 = i1 + 2; if ( i3 >= iPoints ) i3 -= iPoints;
		fXCentre = (pXArray[i1] + pXArray[i2] + pXArray[i3]) / 3.0;
		fYCentre = (pYArray[i1] + pYArray[i2] + pYArray[i3]) / 3.0;
		fAngle1 = GetAngle( fXCentre, fYCentre, pXArray[i1], pYArray[i1] );
		fAngle2 = GetAngle( fXCentre, fYCentre, pXArray[i2], pYArray[i2] );
		fAngle3 = GetAngle( fXCentre, fYCentre, pXArray[i3], pYArray[i3] );
		// Now work out the relative angle positions and make sure all are positive
		fAngle2 -= fAngle1; if ( fAngle2 < 0 ) fAngle2 += 2*M_PI;
		fAngle3 -= fAngle1; if ( fAngle3 < 0 ) fAngle3 += 2*M_PI;
		if ( fAngle2 < fAngle3 )
		{ // Then points are in clockwise order so central one can be eliminated as long as we don't
			// fill an area that we shouldn't
			bool bPointIsWithinTriangle = false;
			if ( iPoints > 3 )
			{ // Need to check that there isn't a point within the area - for convex shapes
				double fLineAngle12 = GetAngle( pXArray[i1], pYArray[i1], pXArray[i2], pYArray[i2] );
				if ( fLineAngle12 < 0 )
					fLineAngle12 += M_PI * 2.0;
				double fLineAngle23 = GetAngle( pXArray[i2], pYArray[i2], pXArray[i3], pYArray[i3] );
				if ( fLineAngle23 < 0 )
					fLineAngle23 += M_PI * 2.0;
				double fLineAngle31 = GetAngle( pXArray[i3], pYArray[i3], pXArray[i1], pYArray[i1] );
				if ( fLineAngle31 < 0 )
					fLineAngle31 += M_PI * 2.0;

				for ( int i = i3+1 ; i != i1 ; i++ )
				{
					if ( i >= iPoints )
					{
						i = 0;
						if ( i1 == 0 )
							break; // From the for loop - finished
					}

					// Otherwise we need to work out whether point i is to right of line  i3 to i1
					double fPointAngle1 = GetAngle( pXArray[i1], pYArray[i1], pXArray[i], pYArray[i] );
					if ( fPointAngle1 < 0 )
						fPointAngle1 += M_PI * 2.0;
					fPointAngle1 -= fLineAngle12;
					if ( fPointAngle1 < 0 )
						fPointAngle1 += M_PI * 2.0;

					double fPointAngle2 = GetAngle( pXArray[i2], pYArray[i2], pXArray[i], pYArray[i] );
					if ( fPointAngle2 < 0 )
						fPointAngle2 += M_PI * 2.0;
					fPointAngle2 -= fLineAngle23;
					if ( fPointAngle2 < 0 )
						fPointAngle2 += M_PI * 2.0;

					double fPointAngle3 = GetAngle( pXArray[i3], pYArray[i3], pXArray[i], pYArray[i] );
					if ( fPointAngle3 < 0 )
						fPointAngle3 += M_PI * 2.0;
					fPointAngle3 -= fLineAngle31;
					if ( fPointAngle3 < 0 )
						fPointAngle3 += M_PI * 2.0;

					if ( ( fPointAngle1 < M_PI ) && ( fPointAngle2 < M_PI ) && ( fPointAngle3 < M_PI ) )
						bPointIsWithinTriangle = true;
				}
			}

			if ( !bPointIsWithinTriangle )
			{// If not then try the next position
				printf( "Draw for points %d, %d, %d of %d available\n", i1, i2, i3, iPoints );
				DrawTriangle( pXArray[i1], pYArray[i1], pXArray[i2], pYArray[i2], 
							pXArray[i3], pYArray[i3], /*GetColour(iPoints)*/uiColour, pTarget );
				// Remove the point i2 and then recurse			
				for ( int i = i2 ; i < (iPoints-1) ; i++ )
				{
					printf( "\tCopy point %d to %d\n", i+1, i );
					pXArray[i] = pXArray[i+1];
					pYArray[i] = pYArray[i+1];
				}
				if ( iPoints > 3 )
					DrawPolygon( iPoints - 1, pXArray, pYArray, uiColour, pTarget );
				return; // Done
			}
		}
	}
}

/* Added in 2014 since it was used in the GroundMovement playback program so may be useful elsewhere too, but students can ignore this */
void BaseEngine::DrawShortenedArrow( int iX1,int iY1,int iX2,int iY2,
	int iShortenedStart,int iShortenedEnd,
	unsigned int uiColour,int iThickness,
	int iHeadSize,SDL_Surface* pTarget )
{
	if ( pTarget == NULL )
		pTarget = m_pActualScreen;

	//iShortenedStart += 10; // Test
	//iShortenedEnd += 10; // Test

	double dAngle1 = GetAngle( iX1,iY1,iX2,iY2 );
	double dAngle2 = dAngle1 + M_PI;

	double dX1 = iX1 + iShortenedStart * cos( dAngle1 );
	double dY1 = iY1 + iShortenedStart * sin( dAngle1 );
	double dX2 = iX2 + iShortenedEnd * cos( dAngle2 );
	double dY2 = iY2 + iShortenedEnd * sin( dAngle2 );

	// First draw the line
	if ( iThickness < 2 )
	{ // Go to the quicker draw function
		DrawLine( dX1,dY1,dX2,dY2,uiColour,pTarget );
	}
	else
	{
		double dX1l = iX1 + iShortenedStart * cos( dAngle1 );
		double dY1l = iY1 + iShortenedStart * sin( dAngle1 );
		double dX2l = iX2 + (iShortenedEnd + iThickness*1.5) * cos( dAngle2 );
		double dY2l = iY2 + (iShortenedEnd + iThickness*1.5) * sin( dAngle2 );

		DrawThickLine( dX1l,dY1l,dX2l,dY2l,uiColour,iThickness,pTarget );
	}

	// Now draw the arrow head.
	// Need three points - one is end of line and others are at 60 degrees from it.

	DrawTriangle(
		dX2,dY2,
		dX2 + iHeadSize * cos( dAngle2 + M_PI / 6.0 ),dY2 + iHeadSize * sin( dAngle2 + M_PI / 6.0 ),
		dX2 + iHeadSize * cos( dAngle2 - M_PI / 6.0 ),dY2 + iHeadSize * sin( dAngle2 - M_PI / 6.0 ),
		uiColour,pTarget );
}

/* Added in 2014 since it was used in the GroundMovement playback program so may be useful elsewhere too, but students can ignore this */
void BaseEngine::DrawShortenedLine( int iX1,int iY1,int iX2,int iY2,
	int iShortenedStart,int iShortenedEnd,
	unsigned int uiColour,int iThickness,SDL_Surface* pTarget )
{
	if ( pTarget == NULL )
		pTarget = m_pActualScreen;

	double dAngle1 = GetAngle( iX1,iY1,iX2,iY2 );
	double dAngle2 = dAngle1 + M_PI;

	// First draw the line
	if ( iThickness < 2 )
	{ // Go to the quicker draw function
		double dX1 = iX1 + iShortenedStart * cos( dAngle1 );
		double dY1 = iY1 + iShortenedStart * sin( dAngle1 );
		double dX2 = iX2 + iShortenedEnd * cos( dAngle2 );
		double dY2 = iY2 + iShortenedEnd * sin( dAngle2 );

		DrawLine( dX1,dY1,dX2,dY2,uiColour,pTarget );
	}
	else
	{
		double dX1l = iX1 + iShortenedStart * cos( dAngle1 );
		double dY1l = iY1 + iShortenedStart * sin( dAngle1 );
		double dX2l = iX2 + iShortenedEnd * cos( dAngle2 );
		double dY2l = iY2 + iShortenedEnd * sin( dAngle2 );

		if ( iX1 == iX2 )
			printf( "Draw shortened line %d,%d to %d,%d shortened by %d,%d is %f,%f %f,%f\n",
			iX1,iY1,iX2,iY2,iShortenedStart,iShortenedEnd,
			dX1l,dY1l,dX2l,dY2l );

		DrawThickLine( dX1l,dY1l,dX2l,dY2l,uiColour,iThickness,pTarget );
	}
}

void BaseEngine::NotifyAllObjects( int iSignalNumber )
{

	if (displayableObjects != NULL)
	{
		for (int i = 0; (*displayableObjects)[i] != NULL; i++)
		{
			(*displayableObjects)[i]->Notify(iSignalNumber);
		}
	}
}

/* Send a specified notification value to all displayable objects and count the number which give a non-zero response. */
int BaseEngine::NotifyAllObjectsGetCountNonZero( int iSignalNumber )
{
	int iReturn = 0;

	if (displayableObjects != NULL)
	{
		for (int i = 0; (*displayableObjects)[i] != NULL; i++)
		{
			if ((*displayableObjects)[i]->Notify(iSignalNumber) != 0)
				iReturn++;
		}
	}

	return iReturn;
}