/* ======================================================= *
 * Copyright 1998-2005 Stephen C. Grubb                    *
 * http://ploticus.sourceforge.net                         *
 * Covered by GPL; see the file ./Copyright for details.   *
 * ======================================================= */

/* PROC VENN - render a venn diagram */

#include "pl.h"

#define TWOPI 6.2831854
#define HALFPI 1.5707963
#define PI 3.141592653589793238462643

extern double GL_rand();
extern int PLG_circle();
static int do_disk();

int
PLP_venndisk()
{
char attr[NAMEMAXLEN], val[256];
char *line, *lineval;
int first, nt, lvp;
double cenx, ceny, area, radius;
char color[COLORLEN];
char leglabel[128];
double densfact;
char outline[128];
double ofs;
int botflag;
double areascale;
int datadriven, xfld, areafld, clrfld;
double yloc;
int irow;
double y;
int solidfill;
int lblfld;
char lbldet[128];
double adjx, adjy;
int align;

TDH_errprog( "pl proc venn" );

cenx = ceny = area = -1.0;
densfact = 1.0;
strcpy( leglabel, "" );
strcpy( color, "red" );
strcpy( outline, "" );
botflag = 0;
areascale = 1.0;
ofs = 0.0;
datadriven = xfld = areafld = clrfld = 0;
yloc = 0.5;
solidfill = 0;
lblfld = 0;
strcpy( lbldet, "" );

/* get attributes.. */

first = 1;
while( 1 ) {
	line = getnextattr( first, attr, val, &lvp, &nt );
	if( line == NULL ) break;
	first = 0;
	lineval = &line[lvp];

	if( stricmp( attr, "location" )==0 ) getcoords( "location", lineval, &cenx, &ceny );
	else if( stricmp( attr, "bottomlocation" )==0 ) {
		getcoords( "bottomlocation", lineval, &cenx, &ceny ); /* given in scale units */
		botflag = 1;
		}
	else if( stricmp( attr, "area" )==0 ) area = atof( val );        /* square inches unless areascale given */
	else if( stricmp( attr, "areascale" )==0 ) areascale = atof( val );        
	else if( stricmp( attr, "color" )==0 ) strcpy( color, val );
	else if( stricmp( attr, "legendlabel" )==0 || stricmp( attr, "label" )==0 ) strcpy( leglabel, lineval );
	else if( stricmp( attr, "labelfield" )==0 ) lblfld = fref( val );
	else if( stricmp( attr, "labeldetails" )==0 ) strcpy( lbldet, lineval );
	else if( stricmp( attr, "density" )==0 ) densfact = atof( val );
	else if( stricmp( attr, "outline" )==0 ) strcpy( outline, lineval );
	else if( stricmp( attr, "dotsize" )==0 ) ofs = atof( val );

	else if( stricmp( attr, "areafld" )==0 ) { areafld = fref( val ); datadriven = 1; }
	else if( stricmp( attr, "xfld" )==0 ) xfld = fref( val );
	else if( stricmp( attr, "colorfld" )==0 ) clrfld = fref( val );
	else if( stricmp( attr, "yloc" )==0 ) yloc = atof( val );
 	else if( stricmp( attr, "solidfill" )==0 ) {
                if( strnicmp( val, YESANS, 1 )==0 ) solidfill = 1;
                else solidfill = 0;
                }


	else Eerr( 1, "attribute not recognized", attr );
	}


/* sanity checks.. */
if( !datadriven ) {
	if( cenx < 0.0 || ceny < 0.0 || area < 0.0 ) return( Eerr( 428, "The attributes x, y, and area must all be specified", "" ) );
	}

if( datadriven && !scalebeenset() ) return( Eerr( 51, "datadriven requires scaled units.. no scaled plotting area has been defined yet w/ proc areadef", "" ));

/* overrides.. */
if( solidfill ) densfact = 0.0;

if( datadriven ) {
	ceny = Ea( Y, yloc );
	for( irow = 0; irow < Nrecords; irow++ ) {
		area = atof( da( irow, areafld-1 ) );
		area *= areascale;
		if( area > 50.0 ) { Eerr( 72405, "skipping a very large disk", da( irow, areafld ) ); continue; }
		radius = sqrt( area / PI );

		if( xfld > 0 ) cenx = Ea( X, fda( irow, xfld-1, X ));
		else cenx = Ea( X, (double)(irow+1) );

		if( clrfld > 0 ) strcpy( color, da( irow, clrfld-1 ));
		y = ceny + radius;  /* so all disk bottoms are on the line.. */
		do_disk( cenx, y, radius, color, densfact, ofs, outline, solidfill );
		if( lblfld > 0 ) {
			double labx, laby;
			textdet( "labeldetails", lbldet, &align, &adjx, &adjy, -2, "R", 1.0 );
			if( align == '?' ) align = 'C';
			labx = cenx+adjx;
			laby = (Elimit( Y, 'l', 'a' )-(Ecurtextheight*2.0))+adjy;
			Emov( labx, laby );
			Edotext( da( irow, lblfld-1 ), align );
			}
		}
	}

else	{   /* individual disks */
	
	/* now do the plotting work.. */
	/* convert area to a radius in inches.. */
	area *= areascale;
	if( area > 50.0 ) return( Eerr( 72405, "disk area is too large.. reduce 'area' or reduce 'areascale'", "" ) );
	
	radius = sqrt( area / PI );
	if( PLS.usingcm ) { cenx /= 2.54; ceny /= 2.54; ofs /= 2.54; radius /= 2.54; }
	
	if( botflag ) ceny += radius;
	
	do_disk( cenx, ceny, radius, color, densfact, ofs, outline, solidfill );
	
	if( leglabel[0] != '\0' ) PL_add_legent( LEGEND_COLOR, leglabel, "", color, "", "" );
	}

return( 0 );
}


/* ----------------------------------------------------------- */
static int
do_disk( cenx, ceny, radius, color_in, densfact, ofs, outline_in, solidfill )
double cenx, ceny, radius, densfact, ofs;
char *color_in, *outline_in;
int solidfill;
{
int i, j, ndots;
char val[100];
double area, dotx, doty, dx, dy, sqrt();
char outline[100], color[40];

area = PI * radius * radius;  /* standardize area into square inches.. we'll use it for dot density */
ndots = (int)(area * 2000 * densfact);
strcpy( outline, outline_in );
strcpy( color, color_in );
if( stricmp( color, "none" )==0 ) {
	densfact = 0.0;
	strcpy( color, "gray(0.7)" );
	}

if( densfact > 0.0 ) {
     sprintf( val, "color=%s width=0.3", color );
     linedet( "dots", val, 1.0 );

     if( Edev == 's' && ofs == 0.0 ) ofs = 0.008;
     for( i = 0; i < ndots; i++ ) {
	/* random location of candidate dot.. */
	dotx = cenx - radius + (GL_rand() * radius * 2);
	doty = ceny - radius + (GL_rand() * radius * 2);
	
	/* if dot is outside the disk skip it.. */
	dx = fabs( dotx - cenx );
        dy = fabs( doty - ceny );
	if( sqrt( (dx*dx) + (dy*dy) ) > (radius-ofs) ) continue;

	if( ofs == 0.0 ) { Emov( dotx, doty ); Elin( dotx, doty+ofs ); }
	else	{   /* randomized tiny line segment direction.. */
		j = (int) (GL_rand() * 3.0);
		if( j == 0 ) { Emov( dotx, doty ); Elin( dotx, doty+ofs ); }
		else if( j == 1 ) { Emov( dotx, doty ); Elin( dotx+ofs, doty+ofs ); }
		else if( j == 2 ) { Emov( dotx, doty ); Elin( dotx+ofs, doty-ofs ); }
		}
	}
    }

if( solidfill ) PLG_circle( cenx, ceny, radius, color, 0, 60 );

if( stricmp( outline, "no" ) != 0 ) {
	if( stricmp( outline, "yes" )==0 ) strcpy( outline, "" ); 
	if( ! GL_slmember( outline, "*color=*" )) { sprintf( val, " color=%s ", color ); strcat( outline, val ); } 
	linedet( "outline", outline, 0.3 );
	PLG_circle( cenx, ceny, radius, "", 1, 60 );
	}
return( 0 );
}



/* ======================================================= *
 * Copyright 1998-2005 Stephen C. Grubb                    *
 * http://ploticus.sourceforge.net                         *
 * Covered by GPL; see the file ./Copyright for details.   *
 * ======================================================= */


syntax highlighted by Code2HTML, v. 0.9.1