/* p_draw.c
 * Copyright (C) 1997-98 Thomas Merz. All rights reserved.
 *
 * PDFlib drawing routines
 */

#include <stdio.h>
#include <math.h>

#include "p_intern.h"

/* Section 8.6.1 Path segment operators */

void
PDF_moveto(PDF *p, float x, float y)
{
    pdf_end_text(p);
    (void) fprintf(p->fp,"%s %s m\n", pdf_float(x), pdf_float(y));
}

void
PDF_lineto(PDF *p, float x, float y)
{
    pdf_end_text(p);
    (void) fprintf(p->fp,"%s %s l\n", pdf_float(x), pdf_float(y));
}

void
PDF_curveto(PDF *p, float x1, float y1, float x2, float y2, float x3, float y3)
{
    pdf_end_text(p);

    if (x2 == x3 && y2 == y3)	/* second c.p. coincides with final point */
	(void) fprintf(p->fp,"%s %s %s %s y\n",
		    pdf_float(x1), pdf_float(y1), pdf_float(x3), pdf_float(y3));
    else			/* general case with four distinct points */
	(void) fprintf(p->fp,"%s %s %s %s %s %s c\n",
		    pdf_float(x1), pdf_float(y1),
		    pdf_float(x2), pdf_float(y2),
		    pdf_float(x3), pdf_float(y3));
}

/* Convenience routines */

/* 
 * "Best fit" BCP approximation for an arc segment as suggested
 * by Berthold K.P.Horn. Thanks BKPH!
 */

#define ARC_MAGIC	0.5523
#define MIN(x, y)	((x) < (y) ? (x) : (y))

/* PDF_arc() assumes that the current point is already at the arc's start */

void
PDF_arc(PDF *p, float x, float y, float r, float alpha, float beta)
{
    float bcp;
    float cos_alpha, cos_beta, sin_alpha, sin_beta;

    if (r < 0) {
	pdf_error(p, PDF_WARN, "Negative circle radius %f", r);
	return;
    }
    
    if (beta - alpha > 90.0)
    {
	/* cut down in 90 degree segments until done */
	PDF_arc(p, x, y, r, alpha, MIN(alpha + 90, beta));
	if (alpha + 90 < beta)
	    PDF_arc(p, x, y, r, alpha + 90, beta);
	return;
    }

    /* speed up special case for quadrants and circles */
    if ((int) alpha == alpha && (int) beta == beta &&
    	beta - alpha == 90 && (int) alpha % 90 == 0)
    {
	/* prune angle values */
	alpha = (int) alpha % 360;
	beta = (int) beta % 360;

	switch ((int) alpha) {
	    case 0:
		PDF_curveto(p, x + r, y + r*ARC_MAGIC, 
			x + r*ARC_MAGIC, y + r, x, y + r);
		break;
	    case 90:
		PDF_curveto(p, x - r*ARC_MAGIC, y + r, 
			x - r, y + r*ARC_MAGIC, x - r, y);
		break;
	    case 180:
		PDF_curveto(p, x - r, y - r*ARC_MAGIC, 
			x - r*ARC_MAGIC, y - r, x, y - r);
		break;
	    case 270:
		PDF_curveto(p, x + r*ARC_MAGIC, y - r, 
			x + r, y - r*ARC_MAGIC, x + r, y);
		break;
	    default:
		break;
	}
	return;
    }

    alpha = alpha * M_PI / 180;		/* convert to radians */
    beta = beta * M_PI / 180;		/* convert to radians */

    /* This formula yields ARC_MAGIC for alpha == 0, beta == 90 degrees */
    bcp = 4/3 * (1 - cos((beta - alpha)/2)) / sin((beta - alpha)/2);
    
    sin_alpha = sin(alpha);
    sin_beta = sin(beta);
    cos_alpha = cos(alpha);
    cos_beta = cos(beta);

    PDF_curveto(p, 
		x + r * (cos_alpha - bcp * sin_alpha),		/* p1 */
		y + r * (sin_alpha + bcp * cos_alpha),
		x + r * (cos_beta + bcp * sin_beta),		/* p2 */
		y + r * (sin_beta - bcp * cos_beta),
		x + r * cos_beta, y + r * sin_beta);		/* p3 */
}

void
PDF_circle(PDF *p, float x, float y, float r)
{
    if (r < 0) {
	pdf_error(p, PDF_WARN, "Negative circle radius %f", r);
	return;
    }

    /* draw four Bezier curves to approximate a circle */
    PDF_moveto(p, x + r, y);
    PDF_curveto(p, x + r, y + r*ARC_MAGIC, x + r*ARC_MAGIC, y + r, x, y + r);
    PDF_curveto(p, x - r*ARC_MAGIC, y + r, x - r, y + r*ARC_MAGIC, x - r, y);
    PDF_curveto(p, x - r, y - r*ARC_MAGIC, x - r*ARC_MAGIC, y - r, x, y - r);
    PDF_curveto(p, x + r*ARC_MAGIC, y - r, x + r, y - r*ARC_MAGIC, x + r, y);
}

void
PDF_rect(PDF *p, float x, float y, float width, float height)
{
    pdf_end_text(p);
    (void) fprintf(p->fp,"%s %s %s %s re\n",
    			pdf_float(x), pdf_float(y),
			pdf_float(width), pdf_float(height));
}

void
PDF_closepath(PDF *p)
{
    pdf_end_text(p);
    (void) fputs("h\n", p->fp);
}

/* Section 8.6.2 Path painting operators */

void
PDF_endpath(PDF *p)
{
    pdf_end_text(p);
    (void) fputs("n\n", p->fp);
}

void
PDF_stroke(PDF *p)
{
    pdf_end_text(p);
    (void) fputs("S\n", p->fp);
}

void
PDF_closepath_stroke(PDF *p)
{
    pdf_end_text(p);
    (void) fputs("s\n", p->fp);
}

void
PDF_fill(PDF *p)
{
    (void) fputs("f\n", p->fp);
    pdf_end_text(p);
}

void
PDF_fill_stroke(PDF *p)
{
    pdf_end_text(p);
    (void) fputs("B\n", p->fp);
}

void
PDF_closepath_fill_stroke(PDF *p)
{
    (void) fputs("b\n", p->fp);
    pdf_end_text(p);
}

/* Section 8.6.3 Path clipping operators */

void
PDF_clip(PDF *p)
{
    pdf_end_text(p);
    (void) fputs("W\n", p->fp);
}
