The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
//file: Polygon/functions.c
/*

Copyright 2004 Eric L. Wilhelm

GPL / Artistic License
See Polygon.pm for details

Version 0.04

*/

#include "gpc.h"
#include "gpc.c"

/*#define DEBUG_PRINT*/

#ifdef DEBUG_PRINT
#define dbg_p(x) printf x
#else
#define dbg_p(x)
#endif

SV* new (char* class);
void add_polygon(SV* obj, SV* pg, int hole);
void DESTROY(SV* obj);
int from_file(SV* obj, char* filename, int want_hole);
void to_file(SV* obj, char* filename, int want_hole);
SV* clip_to(SV* obj, SV* clp, char* action);
void add_polygon(SV* obj, SV* pg, int hole);
void get_polygons(SV* obj);
void pts_to_vertex_list(SV* pg, gpc_vertex_list* vl);
AV* vertex_list_to_pts(gpc_vertex_list* vl);
void gpc_free_polygon2(gpc_polygon *p);

SV* new (char* class) {
	gpc_polygon* p = malloc(sizeof(gpc_polygon));
	SV* obj_ref = newSViv(0);
	SV* obj = newSVrv(obj_ref, class);  // bless it
	p->num_contours = 0;
	sv_setiv(obj, (IV)p);
	SvREADONLY_on(obj);
	return(obj_ref);
}

void DESTROY(SV* obj) {
	gpc_polygon* p = (gpc_polygon*) SvIV(SvRV(obj));
	dbg_p(("running destroy for %d contours\n", p->num_contours));
	if(p->num_contours > 0) {
		dbg_p(("free contents now\n"));
		gpc_free_polygon2(p);
	}
	dbg_p(("that's done now\n"));
	free(p);
	dbg_p(("p free... DESTROY complete\n"));
}

int from_file(SV* obj, char* filename, int want_hole) {
	gpc_polygon* p = (gpc_polygon*) SvIV(SvRV(obj));
	FILE* sfp;
	dbg_p(("from %s file\n", filename));
	sfp = fopen(filename, "r");
	if(! sfp) {
		dbg_p(("file open failed\n"));
		return(0);
	}
	gpc_read_polygon(sfp, want_hole, p);
	dbg_p(("read %d contours\n", p->num_contours));
	return(p->num_contours);
}

void to_file(SV* obj, char* filename, int want_hole) {
	gpc_polygon* p = (gpc_polygon*) SvIV(SvRV(obj));
	FILE* ofp;
	ofp = fopen(filename, "w");
	gpc_write_polygon(ofp, want_hole, p);
}

SV* clip_to(SV* obj, SV* clp, char* action) {
	gpc_polygon* p = (gpc_polygon*) SvIV(SvRV(obj));
	HV* stash = SvSTASH(SvRV(obj));
	// we need the classname to make a new object and to check on clp
	char * classname = HvNAME(stash);
	SV* result = new(classname);
	gpc_polygon* r = (gpc_polygon*) SvIV(SvRV(result));
	gpc_op op;
	gpc_polygon* c;
	if(! sv_isobject(clp)) {
		croak("not an object");
	}
	if(! sv_isa(clp, classname)) {
		croak("not a member of %s", classname);
	}
	c = (gpc_polygon*) SvIV(SvRV(clp));
	if(! strcmp(action, "INTERSECT")) {
		dbg_p(("performing INTERSECT\n"));
		op = GPC_INT;
	}
	if(! strcmp(action, "DIFFERENCE")) {
		dbg_p(("performing DIFFERENCE\n"));
		op = GPC_DIFF;
	}
	if(! strcmp(action, "UNION")) {
		dbg_p(("performing UNION\n"));
		op = GPC_UNION;
	}
	/* FIXME: need some way to integrate this:
		printf("%s is not an operation".
			" (INTERSECT|DIFFERENCE|UNION)\n", action
			);
	*/
	gpc_polygon_clip(op, p, c, r);
	return(result);
}

void add_polygon(SV* obj, SV* pg, int hole) {
	gpc_polygon* p = (gpc_polygon*) SvIV(SvRV(obj));
	dbg_p(("got my vl\n"));
	if(p->num_contours > 0) {
		gpc_vertex_list* c;
		MALLOC(c, sizeof(gpc_vertex_list), 
			"addable contour creation\n");
		pts_to_vertex_list(pg, c);
		dbg_p(("adding to existing\n"));
		gpc_add_contour(p, c, hole);
	}
	else {
		dbg_p(("adding as new\n"));
		MALLOC(p->hole, sizeof(int), "hole flag array\n");
		dbg_p(("setting hole\n"));
		p->hole[0] = hole;
		dbg_p(("making contour\n"));
		MALLOC(p->contour, sizeof(gpc_vertex_list), 
			"contour creation\n");
		pts_to_vertex_list(pg, &(p->contour[0]) );
		dbg_p(("got %d vertices\n", p->contour[0].num_vertices));
		p->num_contours = 1;
	}
	dbg_p(("added\n"));
}

void get_polygons(SV* obj) {
	Inline_Stack_Vars;
	int c;
	gpc_polygon* p = (gpc_polygon*) SvIV(SvRV(obj));
	Inline_Stack_Reset;
	if(p->num_contours < 1) {
		dbg_p(("no contours\n"));
		Inline_Stack_Done;
		return;
	}
	for(c = 0; c < p->num_contours; c++) {
		Inline_Stack_Push(newRV_noinc((SV*) vertex_list_to_pts(&(p->contour[c]))));
	}
	Inline_Stack_Done;


}

void pts_to_vertex_list(SV* pg, gpc_vertex_list* vl) {
	SV** psv;
	SV* val;
	AV* pt;
	AV* pts;
	I32 p;
	I32 num;
	if(!SvROK(pg))
		croak("polygon must be reference\n");
	pts = (AV*)SvRV(pg);
	num = av_len(pts) + 1;
	dbg_p(("going to allocate for %d pts\n", num));
	MALLOC(vl->vertex, num * sizeof(gpc_vertex), "vertex creation");
	vl->num_vertices = num;
	dbg_p(("MALLOC okay (%d vertices)\n", vl->num_vertices));
	for(p = 0; p < num; p++) {
		psv = av_fetch(pts, p, 0);
		val = *psv;
		if(! SvROK(val))
			croak("point %d not a ref", p);
		pt = (AV*)SvRV(val);
		psv = av_fetch(pt, 0, 0);
		val = *psv;
		vl->vertex[p].x = SvNV(val);
		psv = av_fetch(pt, 1, 0);
		val = *psv;
		vl->vertex[p].y = SvNV(val);
		dbg_p(("added %0.2f, %0.2f\n", 
			vl->vertex[p].x, vl->vertex[p].y
			));
	}
	dbg_p(("returning\n"));
}

AV* vertex_list_to_pts(gpc_vertex_list* vl) {
	AV* pts;
	AV* pt;
	int p;
	dbg_p(("%d vertices\n", vl->num_vertices));
	pts = newAV();
	for(p = 0; p < vl->num_vertices; p++) {
		pt = newAV();
		av_push(pts, newRV_noinc((SV*) pt));
		av_push(pt, newSVnv(vl->vertex[p].x));
		av_push(pt, newSVnv(vl->vertex[p].y));
		dbg_p(("point %d: %0.2f, %0.2f\n", 
			p, vl->vertex[p].x, vl->vertex[p].y
			));
	}
	return(pts);
}


void gpc_free_polygon2(gpc_polygon *p) {
	int c;
	for (c= 0; c < p->num_contours; c++) {
  	dbg_p(("free contour %d\n", c));
    FREE(p->contour[c].vertex);
	}
	dbg_p(("free hole\n"));
	FREE(p->hole);
	dbg_p(("free contour\n"));
	FREE(p->contour);
	p->num_contours= 0;
}