A client program using the ImageUtils package to render a Pie Chart
output GIF for near real-time polling results.
NOTE:
   
/** *************************************************************
 *
 * Draw the actual text and bars onto the pie chart.
 *
 * occurrs by generating the appropriate set of CIUPrimitives
 * and drawing them onto the IUGraphicsContext.
 *
 * A cache is used for the CIUText primitives so they can be
 * resused.  These are expensive primitives to generate.
 *
 * @exception Exception On fatal rendering error
 * @throws Exception On fatal rendering error
 *
 */
 public boolean pollUpdate() throws Exception
  {

    CIUCacheEntry cacheEntry = null;      // A cache entry
    CIUPrimitive gp = null;               // The current primitive to draw

    String key = null;
    int NC  = CIUGraphicsContext.NoClip;  // ID for "no clipping"
    int HC  = CIUGraphicsContext.HClip;   // ID for horizontal clipping
    int VC  = CIUGraphicsContext.VClip;   // ID for vertical clipping
    int HVC = CIUGraphicsContext.HVClip;  // IF for clipping in both directions

    int resize = NC;
    int counter = 0;
    boolean horzClip = false;             // Was there horizontal clipping?
    boolean vertClip = false;             // Was there vertical clipping?
    int iStrMaxWidth;                     // Max width [in pixels] of a string
    byte black;                           // The LUT index for black
    int MARGIN = 5;                       // The size [in pixels] of the margin
    int cX = MARGIN;                      // Current x-position
    int cY = MARGIN;                      // Current y-position
    int H;                                // The height of a primitive
    int W;                                // The width of a primitive




    PollDraw.consoleLog("\nPieChart::pollUpdate> begin");


    //
    //  Get the 4 necessary CIUFonts
    //
    this.retrieveIUFonts( m_IUFontHash );


    //
    // Some initializations
    //
    StrMaxWidth = m_width - (MARGIN*2);
    black = (byte)m_cc.toLUTIndex( "000000" );


    //
    // I need a graphics context to draw on
    //
    m_gc = new CIUGraphicsContext( m_width, cY, m_bgcolor, m_cc );
    PollDraw.consoleLog("PieChart::pollUpdate> gc is " + m_width + " by " + cY + " big");


    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Add the title as a child
    if(m_strTitle != null)
    {
	    key = null;
            cacheEntry = null;

            key = ( m_strTitle + m_cfTitle.key() );

            cacheEntry = m_primitiveCache.get( key );

            if ( null == cacheEntry )
            {
		gp = new CIUText( m_strTitle, iStrMaxWidth, m_cfTitle, m_frame, black ).translate( cX, cY );
                m_primitiveCache.add( key, gp );
            }
            else
            {
                gp = (CIUPrimitive)cacheEntry.m_object;
                gp.zero().translate( cX, cY );
            }

            if ( null!=gp )
            {
                m_gc.draw( gp );
                cY += ( gp.height() + MARGIN );
                gp = null;
            }
    }


    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Add the question as a child
    String  question = m_PollQuestionData.getQuestion();
    cacheEntry = null;
    key = null;

        key = ( question + m_cfText.key() );

        cacheEntry = m_primitiveCache.get( key );

        if ( null == cacheEntry )
        {
	    gp = new CIUText( question, iStrMaxWidth, m_cfText, m_frame, black ).translate( cX, cY );
            m_primitiveCache.add( key, gp );
        }
        else
        {
            gp = (CIUPrimitive)cacheEntry.m_object;
            gp.zero().translate( cX, cY );
        }


        m_gc.draw( gp );
        cY += ( gp.height() + MARGIN + MARGIN);
        gp = null;



        // Dereference the poll answers
        PollAnswer[] answers = m_PollQuestionData.getAnswers();
	float top_percent = answers[0].percentage();

        double [] sizes = new double[ answers.length ];
        for( int a = 0; a < answers.length; ++a )
        sizes[a] = (double)answers[a].percentage();



        // Draw The pie chart....
        int radius = (int) ((m_width/2.0) - MARGIN*2);
        gp = new CIUPieChart( radius, false, sizes, m_frame, m_pieColors, "000000", m_bgcolor, m_cc ).translate( cX+MARGIN, cY );
        m_gc.draw( gp );
        cY += ( gp.height() + MARGIN*3 );
        gp = null;





	// Draw the poll answers themselves
	for( int a = 0; a < answers.length; ++a )
	{
	    // Some preparatory values
	    float  fPer = answers[a].percentage();
	    byte  byteColor = (byte)m_cc.toLUTIndex( m_pieColors[a % m_pieColors.length] );
	    int   intColor = CIUBitArray.byteToInt( byteColor );


            PollDraw.consoleLog("PieChart::pollUpdate> color: " + m_pieColors[a % m_pieColors.length] );
            PollDraw.consoleLog("PieChart::pollUpdate> color: " + intColor );


            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Draw percentage string.
            //
            String sPer = getPercentString( fPer );
			cacheEntry = null;
            key = null;

            key = ( sPer + m_cfPercent.key()  );

            cacheEntry = m_primitiveCache.get( key );

            if ( null == cacheEntry )
            {
                PollDraw.consoleLog("PieChart::pollUpdate> Did not find cache entry: " + sPer);
                gp = new CIUText( sPer, 1000, m_cfPercent, m_frame, black ).translate( cX, cY );

            }
            else
            {
                PollDraw.consoleLog("PieChart::pollUpdate> Found cache entry: " + sPer);
                gp = (CIUPrimitive)cacheEntry.m_object;
                gp.zero().translate( cX, cY );
            }


            m_gc.draw( gp );
            cY += gp.height();
            gp = null;



            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Draw the answer
            String answer = answers[a].answer();
            cacheEntry = null;
            key = null;

            key = ( answer + m_cfText.key() + intColor );

            cacheEntry = m_primitiveCache.get( key );

            if ( null == cacheEntry )
            {
                PollDraw.consoleLog("PieChart::pollUpdate> Did not find cache entry: " + answer);
                gp = new CIUText( answer, iStrMaxWidth, m_cfText, m_frame, byteColor ).translate( cX, cY );
                m_primitiveCache.add( key, gp );
            }
            else
            {
                PollDraw.consoleLog("PieChart::pollUpdate> Found cache entry: " + answer);
                gp = (CIUPrimitive)cacheEntry.m_object;
                gp.zero().translate( cX, cY );
            }


            m_gc.draw( gp );
            cY += ( MARGIN + gp.height() );
            gp = null;
    	}


        // Translate
        cY += MARGIN;




        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Draw the string for the total votes...
	String total = "Total Votes: ";
	cacheEntry = null;
        key = null;

        key = ( total + m_cfTotal.key() );

        cacheEntry = m_primitiveCache.get( key );

        if ( null == cacheEntry )
        {
            PollDraw.consoleLog("PieChart::pollUpdate> Did not find cache entry: " + total);
            gp = new CIUText( total, iStrMaxWidth, m_cfTotal, m_frame, black ).translate( cX, cY );
            m_primitiveCache.add( key, gp );
        }
        else
        {
            PollDraw.consoleLog("PieChart::pollUpdate> Found cache entry: " + total);
            gp = (CIUPrimitive)cacheEntry.m_object;
            gp.zero().translate( cX, cY );
        }

        m_gc.draw( gp );
        W = gp.width();
        gp = null;




	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Draw the string for the number of votes...
        //
	String votes = commaAbsInteger( m_PollQuestionData.totalVotes() );
        cacheEntry = null;
        key = null;

        key = ( votes + m_cfTotal.key() );

        cacheEntry = m_primitiveCache.get( key );

        if ( null == cacheEntry )
        {
            PollDraw.consoleLog("PieChart::pollUpdate> Did not find cache entry: " + votes );
            gp = new CIUText( votes, iStrMaxWidth, m_cfTotal, m_frame, black ).translate( (cX + W), cY );
            m_primitiveCache.add( key, gp );
        }
        else
        {
            PollDraw.consoleLog("PieChart::pollUpdate> Found cache entry: " + votes );
            gp = (CIUPrimitive)cacheEntry.m_object;
            gp.zero().translate( (cX + W), cY );
        }


        m_gc.draw( gp );
        cY += ( MARGIN + gp.height() );
        gp = null;

        cacheEntry = null;


        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //
        // Resize the GC so we know it is big enough....
        //
        resize = m_gc.resize( m_width, cY );
        if ( VC == resize ) { vertClip=true; }
        else if ( HC == resize ) { horzClip=true; }
        else if ( HVC == resize ) { horzClip=true; vertClip=true; }

        PollDraw.consoleLog("PieChart::pollUpdate> gc is " + m_width + " by " + cY + " big");
        PollDraw.consoleLog("PieChart::pollUpdate> clip: h:" + horzClip + "  v:" + vertClip );



        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //
        // For very small polls,  the GC can clip the percentage text.
        // Try to resize horizontally 3 times... if we need any more
        // than 3 tries (15 pixels),  then there's more wrong here
        // than simple resizing.
        //
        while ( (HC==resize) && (counter<4) )
        {
            ++counter;

            m_width+=MARGIN;
            resize = m_gc.resize( m_width, cY );

            if ( VC == resize ) { vertClip=true; }
            else if ( HC == resize ) { horzClip=true; }
            else if ( HVC == resize ) { horzClip=true; vertClip=true; }

            PollDraw.consoleLog("PieChart::pollUpdate> gc is " + m_width + " by " + cY + " big");
            PollDraw.consoleLog("PieChart::pollUpdate> clip: h:" + horzClip + "  v:" + vertClip );
        }


       PollDraw.consoleLog("PieChart::pollUpdate> end\n");

       return true;
     }