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

// This file is part of the CAD::Drawing::IO::DWGI package Copyright
// 2003-2006 Eric Wilhelm.  This Perl module is free software, made
// available AS-IS and distributed under the same terms as Perl.  See
// the main module file (DWGI.pm) for details.
//
// This is a set of functions which wrap the OpenDWG toolkit so that it
// can be called from Perl programs.  There is no connection between the
// licensing of this software and the licensing of the OpenDWG toolkit.
// You must obtain and install the toolkit under the licensing terms of
// the OpenDWG consortium in order to use this module, as their
// licensing prohibits distribution of the library and header files
// (though they are free to use if you join the Consortium as an
// Associate (or higher) member.)
//
// See http://www.opendwg.org for details.
//
// While the Author is an Associate members of the OpenDWG consortium
// for purposes of using the OpenDWG libraries, this is the full extent
// of the relationship.
//
// By using this software, you agree to not hold any party liable for
// anything which your use of this software causes to happen to you,
// your dog, your business, or anything else.  You also agree that no
// entity or person is responsible for anything that any other entity or
// person has caused to happen and that anything which you do (including
// using this software and obtaining the appropriate license and
// libraries for it) is YOUR OWN RESPONSIBILITY.


#define AD_PROTOTYPES
#include "ad2.h"
#define OD_GENERIC_READ
#include "odio.h"



typedef struct {
	PAD_DWGHDR   adhd;
	PAD_ENT_HDR  adenhd;
	PAD_ENT      aden;
	PAD_TB       adtb;
	PAD_XD       adxd;
	AD_OBJHANDLE default_ltype; // default linetype handle
	AD_OBJHANDLE default_layer; // default layer handle
	AD_OBJHANDLE current_layer; // current layer handle
	AD_VMADDR    entitylist;
	AD_DB_HANDLE handle;
	bool         file_is_open;
	short        version;
} DWGstruct;

const char initfilepath[]="/usr/local/stow/openDWG/adinit/adinit.dat";
short initerror;

short criterrhandler(short num);
int allocateadptrs(DWGstruct* dwg);
/*-------------------------Greeting-----------------------------------*/
void hello() {
	printf("hello world\n");
	}

SV*	new (char* class) {
	DWGstruct* dwg = malloc(sizeof(DWGstruct));
	SV*        obj_ref = newSViv(0);
	SV*        obj = newSVrv(obj_ref, class); // creates the blessed reference
	//adSetAd2CriticalErrorFn(criterrhandler);
	if(!adInitAd2(initfilepath,&initerror)) {
		printf("failing miserably\n");
		return(&PL_sv_undef);
		}
	if(!allocateadptrs(dwg))
		return(&PL_sv_undef);
	dwg->file_is_open = 0;
	sv_setiv(obj, (IV)dwg);
	SvREADONLY_on(obj);
	return(obj_ref);
	};


int loadfile(SV* obj, char* infile) {
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	adSetupDwgRead();
	adSetupDxfRead();
	//printf("loading file\n");
	if(dwg->handle = adLoadFile(infile,AD_PRELOAD_ALL,1)) {
		//printf("loaded okay\n");
		dwg->file_is_open = 1;
		return(1);
	}
	croak("loading failed miserably\n");
	return(0);
} // loadfile

int closefile(SV* obj) {
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	if(dwg->file_is_open) {
		adCloseFile(dwg->handle);
		dwg->file_is_open = 0;
		//printf("closing\n");
	}
} // closefile

int newfile(SV* obj, short version) {
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	AD_LTYPE templtp;
	dwg->handle = adNewFile(NULL, NULL, 0, version);
	dwg->adhd = adHeaderPointer(dwg->handle);
	dwg->adhd->dwgcodepage = 30;  // get rid of NLS error
	adFindLayerByName(dwg->handle, "0", dwg->default_layer);
	adHancpy(dwg->current_layer, dwg->default_layer);
	adStartLinetypeGet(dwg->handle);
	adGetLinetype( dwg->handle, &templtp);
	adHancpy(dwg->default_ltype, templtp.objhandle);
	dwg->version = version;
	dwg->file_is_open = 1;
} // newfile

short savefile(SV* obj, char* filename, short filetype) {
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	short dxfnegz= 0;
	short dxfdecprec= 6;
	short dxfwritezeroes = 1;
	char r12dxfvbls= 1;
	if( filetype != AD_DWG) {
		// setup options for dxf write
		dxfnegz= 1;
		dxfdecprec = 14;
		dxfwritezeroes = 1;
		r12dxfvbls= 1; // not writing R11 DXF
	}
	adSaveFile (dwg->handle, filename, (char) filetype, dwg->version, 
					dxfnegz, dxfdecprec, dxfwritezeroes, r12dxfvbls);
} // savefile

/*-------------------------listlayers---------------------------------*/

void listlayers(SV* obj) {
	Inline_Stack_Vars;
	long num;
	long i;
	long len;
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	PAD_TB adtb = dwg->adtb;
	Inline_Stack_Reset;
	adStartLayerGet(dwg->handle);
	num = adNumLayers(dwg->handle);
	for(i=0;i<num;i++) {
		SV* tmpsv;
		adGetLayer(dwg->handle, &adtb->lay);
		if(! adtb->lay.purgedflag) {
			//printf("\tshould see %s\n", adtb->lay.name);
			len = strlen(adtb->lay.name);
			tmpsv = newSVpvn(adtb->lay.name, len);
			Inline_Stack_Push(sv_2mortal(tmpsv));
			}
		}
	Inline_Stack_Done;		
	}
/*--------------------------------------------------------------------*/

int writeLayer(SV* obj, SV* args) {
	short color;
	char * name;
	SV** psv;
	SV* val;
	STRLEN len;
	HV* hash;
	HE* entry;
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));

	if(! SvROK(args))
		croak("args is not a reference");

	hash = (HV*)SvRV(args);
	if(hv_exists(hash, "name", 4)) {
		psv = hv_fetch(hash, "name", 4, 0);
		val = *psv;
		len = SvLEN(val);
		name = SvPV(val, len);
	}
	else {
		croak("layer name is required");
	}
	adSetDefaultLayer(dwg->handle, &dwg->adtb->lay);
	strcpy(dwg->adtb->lay.name, name);
	if(hv_exists(hash, "color", 5)) {
		psv = hv_fetch(hash, "color",  5, 0);
		val = *psv;
		color = SvIV(val);
	}
	else {
		color = AD_COLOR_WHITE;
	}
	dwg->adtb->lay.color = color;
	// need to add support  for more linetypes later
	adHancpy(dwg->adtb->lay.linetypeobjhandle, dwg->default_ltype);
	adGenerateObjhandle(dwg->handle,dwg->adtb->lay.objhandle);
	// set the current layer handle to this layer
	adHancpy(dwg->current_layer, dwg->adtb->lay.objhandle);
	adAddLayer(dwg->handle,&dwg->adtb->lay);
} // writeLayer

int setLayer(SV * obj, char * name) {
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	if(adFindLayerByName(dwg->handle, name, dwg->current_layer));
		return(1);
	return(0);
	}

SV* getCircle(SV* obj) {
	AV * pt;
	HV * hash = newHV();
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	//printf("circle at: %3.2f,%3.2f\n", dwg->aden->circle.pt0[0], dwg->aden->circle.pt0[1]);
	hv_store(hash, "pt", 2, newRV_noinc((SV*)pt = newAV()), 0);
	av_push(pt, newSVnv(dwg->aden->circle.pt0[0]));
	av_push(pt, newSVnv(dwg->aden->circle.pt0[1]));
	av_push(pt, newSVnv(dwg->aden->circle.pt0[2]));
	hv_store(hash, "rad", 3, newSVnv(dwg->aden->circle.radius), 0);
	return(newRV_noinc((SV*) hash));
}

int writeCircle(SV* obj, SV* args) {
	HV* hash;
	AV* pt;
	SV** psv;
	SV** psv2;
	SV* val;
	int i;
	I32 len;
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	dwg->adenhd->enttype = AD_ENT_CIRCLE;
	adSetEntityDefaults( dwg->handle, dwg->adenhd, dwg->aden);
	adGenerateObjhandle(dwg->handle, dwg->adenhd->enthandle);
	if(! SvROK(args))
		croak("args is not a reference");
	// printf("writing circle\n");
	hash = (HV*)SvRV(args);
	if(hv_exists(hash, "pt", 2)) {
		psv = hv_fetch(hash, "pt", 2, 0);
		val = *psv;
		pt = (AV*)SvRV(val);
		len = av_len(pt);
		//printf("array length: %d\n", len);
		for(i=0;i<3;i++) {
			if(av_exists(pt, i)) {
				psv2 = av_fetch(pt, i, 0);
				val = *psv2;
				dwg->aden->circle.pt0[i] = SvNV(val);
				//printf("point %d:  %4.2f\n", i, dwg->aden->circle.pt0[i]);
			}
			else {
				dwg->aden->circle.pt0[i] = 0;
			}
		}
	}
	else {
		croak("no pt passed");
	}
	if(hv_exists(hash, "rad", 3)) {
		//printf("fetching\n");
		psv = hv_fetch(hash, "rad", 3,0);
		//printf("deref\n");
		val = *psv;
		//printf("setting\n");
/*        if(! (SvNOK(val) || SvIOK(val)))*/
/*            croak("not a number");*/
		dwg->aden->circle.radius = SvNV(val);
		//printf("set radius to %3.2f\n", dwg->aden->circle.radius);
	}
	else {
		croak("no  rad passed");
	}

	// FIXME: SPOT could be applied (via C) here:
	// to set circle direction, use dwg->adenhd->extrusion[0], [1], and [2]
	set_extrusion(dwg, hash);

	// FIXME: SPOT could be applied (via C) here:
	if(hv_exists(hash, "color", 5)) {
		psv = hv_fetch(hash, "color", 5, 0);
		val = *psv;
		dwg->adenhd->entcolor = SvIV(val);
	}
	else {
		dwg->adenhd->entcolor = AD_COLOR_BYLAYER;
	}

	adHancpy(dwg->adenhd->entlayerobjhandle, dwg->current_layer);
	//printf("copy ok\n");
	adAddEntityToList( dwg->handle, dwg->entitylist, dwg->adenhd, dwg->aden);
} // writeCircle

SV* getEllipse(SV* obj) {
	AV * pt;
	AV * off;
	AV * angs;
	double ang;
	HV * hash = newHV();
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	hv_store(hash, "pt", 2, newRV_noinc((SV*)pt = newAV()), 0);
	av_push(pt, newSVnv(dwg->aden->ellipse.pt0[0]));
	av_push(pt, newSVnv(dwg->aden->ellipse.pt0[1]));
	av_push(pt, newSVnv(dwg->aden->ellipse.pt0[2]));
	hv_store(hash, "off", 3, newRV_noinc((SV*)off = newAV()), 0);
	av_push(off, newSVnv(dwg->aden->ellipse.pt1offset[0]));
	av_push(off, newSVnv(dwg->aden->ellipse.pt1offset[1]));
	av_push(off, newSVnv(dwg->aden->ellipse.pt1offset[2]));
/*    ang = adEllipseAngleFromParameter(*/
/*            dwg->aden->ellipse.startparam, */
/*            dwg->aden->ellipse.minortomajorratio*/
/*            );*/
/*    printf("start ang is %0.4f\n", ang);*/
/*    ang = adEllipseAngleFromParameter(*/
/*            dwg->aden->ellipse.endparam, */
/*            dwg->aden->ellipse.minortomajorratio*/
/*            );*/
/*    printf("end ang is %0.4f\n", ang);*/
	hv_store(hash, "ratio", 5, newSVnv(dwg->aden->ellipse.minortomajorratio), 0);
	hv_store(hash, "angs", 4, newRV_noinc((SV*)angs = newAV()), 0);
	av_push(angs, newSVnv(dwg->aden->ellipse.startparam));
	av_push(angs, newSVnv(dwg->aden->ellipse.endparam));
	return(newRV_noinc((SV*) hash));
} // getEllipse

SV* getArc(SV* obj) {
	AV * pt;
	AV * angs;
	HV * hash = newHV();
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	hv_store(hash, "pt", 2, newRV_noinc((SV*)pt = newAV()), 0);
	av_push(pt, newSVnv(dwg->aden->arc.pt0[0]));
	av_push(pt, newSVnv(dwg->aden->arc.pt0[1]));
	av_push(pt, newSVnv(dwg->aden->arc.pt0[2]));
	hv_store(hash, "rad", 3, newSVnv(dwg->aden->arc.radius), 0);
	hv_store(hash, "angs", 4, newRV_noinc((SV*)angs = newAV()), 0);
	av_push(angs, newSVnv(dwg->aden->arc.stang));
	av_push(angs, newSVnv(dwg->aden->arc.endang));
	return(newRV_noinc((SV*) hash));
} // getArc

int writeArc(SV* obj, SV* args) {
	HV* hash;
	AV* pt;
	AV* angs;
	SV** psv;
	SV* val;
	int i;
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	dwg->adenhd->enttype = AD_ENT_ARC;
	adSetEntityDefaults( dwg->handle, dwg->adenhd, dwg->aden);
	adGenerateObjhandle(dwg->handle, dwg->adenhd->enthandle);
	if(! SvROK(args))
		croak("args is not a reference");
	hash = (HV*)SvRV(args);
	if(hv_exists(hash, "pt", 2)) {
		psv = hv_fetch(hash, "pt", 2, 0);
		val = *psv;
		pt = (AV*)SvRV(val);
		for(i=0;i<3;i++) {
			if(av_exists(pt, i)) {
				psv = av_fetch(pt, i, 0);
				val = *psv;
				dwg->aden->arc.pt0[i] = SvNV(val);
			}
			else {
				dwg->aden->arc.pt0[i] = 0;
			}
		}
	}
	else {
		croak("no pt passed");
	}
	if(hv_exists(hash, "rad", 3)) {
		psv = hv_fetch(hash, "rad", 3,0);
		val = *psv;
/*        if(! (SvNOK(val) || SvIOK(val)))*/
/*            croak("not a number");*/
		dwg->aden->arc.radius = SvNV(val);
	}
	else {
		croak("no  rad passed");
	}
	if(hv_exists(hash, "angs", 4)) {
		psv = hv_fetch(hash, "angs", 4, 0);
		val = *psv;
		if(!SvROK(val))
			croak("angs not a reference");
		angs = (AV*)SvRV(val);
		psv = av_fetch(angs, 0, 0);
		val = *psv;
		dwg->aden->arc.stang = SvNV(val);
		psv = av_fetch(angs, 1, 0);
		val = *psv;
		dwg->aden->arc.endang = SvNV(val);
	}
	else {
		croak("no angs passed");
	}

	set_extrusion(dwg, hash);

	// FIXME: SPOT could be applied (via C) here:
	if(hv_exists(hash, "color", 5)) {
		psv = hv_fetch(hash, "color", 5, 0);
		val = *psv;
		dwg->adenhd->entcolor = SvIV(val);
	}
	else {
		// FIXME: think this is redundant after set_entity_defaults
		dwg->adenhd->entcolor = AD_COLOR_BYLAYER;
	}

	adHancpy(dwg->adenhd->entlayerobjhandle, dwg->current_layer);
	adAddEntityToList( dwg->handle, dwg->entitylist, dwg->adenhd, dwg->aden);
} // writeArc

SV* getLine(SV* obj) {
	int i;
	AV * pts;
	AV * pt0;
	AV * pt1;
	HV * hash = newHV();
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	hv_store(hash, "pts", 3, newRV_noinc((SV*)pts = newAV()), 0);
	av_push(pts, newRV_noinc((SV*)pt0 = newAV()));
	av_push(pts, newRV_noinc((SV*)pt1 = newAV()));
	for(i=0;i<3;i++) {
		av_push(pt0, newSVnv(dwg->aden->line.pt0[i]));
	}
	for(i=0;i<3;i++) {
		av_push(pt1, newSVnv(dwg->aden->line.pt1[i]));
	}
	return(newRV_noinc((SV*) hash));
} // getLine

int writeLine(SV* obj, SV* args) {
	HV* hash;
	AV* pt;
	AV* pts;
	SV** psv;
	SV* val;
	int i;
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	dwg->adenhd->enttype = AD_ENT_LINE;
	adSetEntityDefaults( dwg->handle, dwg->adenhd, dwg->aden);
	adGenerateObjhandle(dwg->handle, dwg->adenhd->enthandle);
	if(! SvROK(args))
		croak("args is not a reference");
	hash = (HV*)SvRV(args);
	if(hv_exists(hash, "pts", 3)) {
		psv = hv_fetch(hash, "pts", 3, 0);
		val = *psv;
		if(!SvROK(val))
			croak("pts not a reference");
		pts = (AV*)SvRV(val);
		psv = av_fetch(pts, 0, 0);
		val = *psv;
		if(!SvROK(val))
			croak("pt not a reference");
		pt = (AV*)SvRV(val);
		for(i=0;i<3;i++) {
			if(av_exists(pt, i)) {
				psv = av_fetch(pt, i, 0);
				val = *psv;
				dwg->aden->line.pt0[i] = SvNV(val);
			}
			else {
				dwg->aden->line.pt0[i] = 0;
			}
		}
		psv = av_fetch(pts, 1, 0);
		val = *psv;
		if(!SvROK(val))
			croak("pt not a reference");
		pt = (AV*)SvRV(val);
		for(i=0;i<3;i++) {
			if(av_exists(pt, i)) {
				psv = av_fetch(pt, i, 0);
				val = *psv;
				dwg->aden->line.pt1[i] = SvNV(val);
			}
			else {
				dwg->aden->line.pt1[i] = 0;
			}
		}
	} // end if "pts" key
	else {
		croak("pts not passed");
	}
	if(hv_exists(hash, "color", 5)) {
		psv = hv_fetch(hash, "color", 5, 0);
		val = *psv;
		dwg->adenhd->entcolor = SvIV(val);
	}
	else {
		dwg->adenhd->entcolor = AD_COLOR_BYLAYER;
	}

	adHancpy(dwg->adenhd->entlayerobjhandle, dwg->current_layer);
	adAddEntityToList( dwg->handle, dwg->entitylist, dwg->adenhd, dwg->aden);
} // writeLine

SV* getText(SV* obj) {
	int i;
	long len;
	AV * pt;
	HV * hash = newHV();
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	hv_store(hash, "pt", 2, newRV_noinc((SV*)pt = newAV()), 0);
	for(i=0;i<3;i++) {
		av_push(pt, newSVnv(dwg->aden->text.pt0[i]));
	}
	len = strlen(dwg->aden->text.textstr);
	hv_store(hash, "string", 6, newSVpvn(dwg->aden->text.textstr, len), 0);
	// now for the long-awaited text height!
	hv_store(hash, "height", 6, newSVnv(dwg->aden->text.tdata.height), 0);
	if(dwg->aden->text.tdata.rotang) {
		// the text is rotated
		hv_store(hash, "angle", 5, newSVnv(dwg->aden->text.tdata.rotang), 0);
	}
	return(newRV_noinc((SV*) hash));
} // getText

int writeText(SV* obj, SV* args) {
	HV* hash;
	AV* pt;
	SV** psv;
	SV* val;
	int i;
	STRLEN len;
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	dwg->adenhd->enttype = AD_ENT_TEXT;
	adSetEntityDefaults( dwg->handle, dwg->adenhd, dwg->aden);
	adGenerateObjhandle(dwg->handle, dwg->adenhd->enthandle);
	if(! SvROK(args))
		croak("args is not a reference");
	hash = (HV*)SvRV(args);
	if(hv_exists(hash, "pt", 2)) {
		psv = hv_fetch(hash, "pt", 2, 0);
		val = *psv;
		pt = (AV*)SvRV(val);
		for(i=0;i<3;i++) {
			if(av_exists(pt, i)) {
				psv = av_fetch(pt, i, 0);
				val = *psv;
				dwg->aden->text.pt0[i] = SvNV(val);
			}
			else {
				dwg->aden->text.pt0[i] = 0;
			}
		}
	}
	else {
		croak("no pt passed");
	}
	if(hv_exists(hash, "string", 6)) {
		psv = hv_fetch(hash, "string", 6, 0);
		val = *psv;
		len = SvLEN(val);
		strcpy(dwg->aden->text.textstr, SvPV(val, len));
	}
	else {
		croak("no string passed");
	}
	if(hv_exists(hash, "height", 6)) {
		psv = hv_fetch(hash, "height", 6, 0);
		val = *psv;
		dwg->aden->text.tdata.height = SvNV(val);
	}
	else {
		// let a default height be used
		dwg->aden->text.tdata.height = 1;
	}

	set_extrusion(dwg, hash);

	if(hv_exists(hash, "color", 5)) {
		psv = hv_fetch(hash, "color", 5, 0);
		val = *psv;
		dwg->adenhd->entcolor = SvIV(val);
	}
	else {
		dwg->adenhd->entcolor = AD_COLOR_BYLAYER;
	}

	adHancpy(dwg->adenhd->entlayerobjhandle, dwg->current_layer);
	adAddEntityToList( dwg->handle, dwg->entitylist, dwg->adenhd, dwg->aden);
} // writeText

SV* getSolid(SV* obj) {
	int i;
	AV * pt;
	char str[512];
	char ch;
	long blb_size;
	PAD_BLOB_CTRL bcptr;
	PAD_BLOB_CTRL bcptr2;
	SV * acis;
	SV * img;
	HV * hash = newHV();
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	acis = newSVpv("", 0);
	img = newSVpv("", 0);
	hv_store(hash, "acis", 4, acis, 0);
	hv_store(hash, "img", 3, img, 0);
	hv_store(hash, "pt", 2, newRV_noinc((SV*)pt = newAV()), 0);
	for(i=0;i<3;i++) {
		av_push(pt, newSVnv(dwg->aden->acisobj.pt0[i]));
	}
	bcptr=adStartBlobRead(dwg->aden->acisobj.ldblob);
	while(adReadAcisString(bcptr, str)) {
		//printf("read %s\n", str);
		//printf("length is %d\n", strlen(str));
		sv_catpvf(acis, "%s\n", str);
	}
	adEndBlobRead(bcptr);
	bcptr2=adStartBlobRead(dwg->aden->acisobj.imgdata);
	printf("blob is %d bytes long\n", adBlobSize(bcptr2));
	while(adReadBlobByte(bcptr2, &ch)) {
		sv_catpvn(img, &ch, 1);
	}
	adEndBlobRead(bcptr2);
	return(newRV_noinc((SV*) hash));
}

SV* getPoint(SV* obj) {
	int i;
	AV * pt;
	HV * hash = newHV();
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	hv_store(hash, "pt", 2, newRV_noinc((SV*)pt = newAV()), 0);
	for(i=0;i<3;i++) {
		av_push(pt, newSVnv(dwg->aden->point.pt0[i]));
	}
	return(newRV_noinc((SV*) hash));
} // getPoint

int writePoint(SV* obj, SV* args) {
	HV* hash;
	AV* pt;
	SV** psv;
	SV* val;
	int i;
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	dwg->adenhd->enttype = AD_ENT_POINT;
	adSetEntityDefaults( dwg->handle, dwg->adenhd, dwg->aden);
	adGenerateObjhandle(dwg->handle, dwg->adenhd->enthandle);
	if(! SvROK(args))
		croak("args is not a reference");
	hash = (HV*)SvRV(args);
	if(hv_exists(hash, "pt", 2)) {
		psv = hv_fetch(hash, "pt", 2, 0);
		val = *psv;
		pt = (AV*)SvRV(val);
		for(i=0;i<3;i++) {
			if(av_exists(pt, i)) {
				psv = av_fetch(pt, i, 0);
				val = *psv;
				dwg->aden->point.pt0[i] = SvNV(val);
			}
			else {
				dwg->aden->point.pt0[i] = 0;
			}
		}
	}
	else {
		croak("no pt passed");
	}

	set_extrusion(dwg, hash);

	if(hv_exists(hash, "color", 5)) {
		psv = hv_fetch(hash, "color", 5, 0);
		val = *psv;
		dwg->adenhd->entcolor = SvIV(val);
	}
	else {
		dwg->adenhd->entcolor = AD_COLOR_BYLAYER;
	}

	adHancpy(dwg->adenhd->entlayerobjhandle, dwg->current_layer);
	adAddEntityToList( dwg->handle, dwg->entitylist, dwg->adenhd, dwg->aden);
} // writePoint

SV* getLWPline(SV* obj) {
	PAD_BLOB_CTRL bcptr;
	OdaLong il;
	long num;
	double tempdouble[2];
	double tempbulge;
	double tempwidth[2];
	AV * pts;
	AV * pt;
	HV * hash = newHV();
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	num = (long) dwg->aden->lwpline.numpoints;
	bcptr = adStartBlobRead(dwg->aden->lwpline.ldblob);
	hv_store(hash, "pts", 3, newRV_noinc((SV*)pts = newAV()), 0);
	for(il=0;il < num; il++) {
		adReadBlob2Double(bcptr, tempdouble);
		if(dwg->aden->lwpline.flag & AD_LWPLINE_HAS_BULGES) {
			adReadBlobDouble(bcptr, &tempbulge);
			// XXX do something with this!
			// printf("bulge! %0.6f\n", tempbulge);
		}
		if (dwg->aden->lwpline.flag & AD_LWPLINE_HAS_WIDTHS) {
			adReadBlob2Double(bcptr,tempwidth);
		}
		// printf("points: %3.2f,%3.2f\n", tempdouble[0], tempdouble[1]);
		av_push(pts, newRV_noinc((SV*)pt = newAV()));
		av_push(pt, newSVnv(tempdouble[0]));
		av_push(pt, newSVnv(tempdouble[1]));
	}
	if(dwg->aden->lwpline.flag & AD_LWPLINE_IS_CLOSED) {
		hv_store(hash, "closed", 6, newSViv(1), 0);
	}
	else {
		hv_store(hash, "closed", 6, newSViv(0), 0);
	}

	return(newRV_noinc((SV*) hash));
} // getLWPline

int writeLWPline(SV* obj, SV* args) {
	HV* hash;
	AV* pt;
	AV* pts;
	SV** psv;
	SV* val;
	int i;
	I32 p;
	I32 num;
	double point[2];
	PAD_BLOB_CTRL bcptr;
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	dwg->adenhd->enttype = adLwplineEnttype(dwg->handle);
	adSetEntityDefaults( dwg->handle, dwg->adenhd, dwg->aden);
	adGenerateObjhandle(dwg->handle, dwg->adenhd->enthandle);
	if(! SvROK(args))
		croak("args is not a reference");
	hash = (HV*)SvRV(args);
	if(hv_exists(hash, "pts", 3)) {
		dwg->aden->lwpline.ldblob = adCreateBlob();
		bcptr = adStartBlobWrite(dwg->aden->lwpline.ldblob);
		psv = hv_fetch(hash, "pts", 3, 0);
		val = *psv;
		if(!SvROK(val))
			croak("pts not a reference");
		pts = (AV*)SvRV(val);
		num = av_len(pts);
		dwg->aden->lwpline.numpoints = (long) num + 1;
		//	printf("lwpline with %d points\n", num+1);
		for(p = 0; p <= num; p++) {
			psv = av_fetch(pts, p, 0);
			val = *psv;
			if(!SvROK(val))
				croak("point is not a reference");
			pt = (AV*)SvRV(val);
			for(i = 0; i < 2;i++) {
				psv = av_fetch(pt, i, 0);
				val = *psv;
				//	printf("adding point %d #%d\n", p, i);
				point[i] = SvNV(val);
			}
			adWriteBlobBytes(bcptr, point, 2 *sizeof(double) );
		}
		adEndBlobWrite(bcptr);
	}
	else {
		croak("pts not passed");
	}
	if(hv_exists(hash, "closed", 6)) {
		psv = hv_fetch(hash, "closed", 6, 0);
		val = *psv;
		if(sv_true(val)) {
			//printf("closing in output (%d)\n", SvIV(val));
			dwg->aden->lwpline.flag |= AD_LWPLINE_IS_CLOSED;
		}
	}

	set_extrusion(dwg, hash);

	if(hv_exists(hash, "elevation", 9)) {
		psv = hv_fetch(hash, "elevation", 9, 0);
		val = *psv;
		dwg->aden->lwpline.elevation = SvNV(val);
		// printf("elevation is %0.4f\n", dwg->aden->lwpline.elevation);
	}

	if(hv_exists(hash, "color", 5)) {
		psv = hv_fetch(hash, "color", 5, 0);
		val = *psv;
		dwg->adenhd->entcolor = SvIV(val);
	}
	else {
		dwg->adenhd->entcolor = AD_COLOR_BYLAYER;
	}
	adHancpy(dwg->adenhd->entlayerobjhandle, dwg->current_layer);
	adAddEntityToList( dwg->handle, dwg->entitylist, dwg->adenhd, dwg->aden);
} // writeLWPline 

SV* getImage(SV* obj) {
	PAD_BLOB_CTRL bcptr;
	int i;
	long il;
	long len;
	double tempdouble[2];
	AD_OBJHANDLE defhandle;
	AD_OBJ_HDR adobhd;
	AD_OBJ adob;
	long num;
	AV * cpts;
	AV * pt;
	HV * hash = newHV();
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	hv_store(hash, "pt", 2, newRV_noinc((SV*)pt = newAV()), 0);
	for(i=0;i<2;i++) {
		av_push(pt, newSVnv(dwg->aden->image.pt0[i]));
	}
	hv_store(hash, "size", 4, newRV_noinc((SV*)pt = newAV()), 0);
	for(i=0;i<2;i++) {
		// printf("image size (%d) is %0.0f\n", i, dwg->aden->image.size[i]);
		av_push(pt, newSVnv(dwg->aden->image.size[i]));
	}
	hv_store(hash, "uvec", 4, newRV_noinc((SV*)pt = newAV()), 0);
	for(i=0;i<2;i++) {
		av_push(pt, newSVnv(dwg->aden->image.uvec[i]));
	}
	hv_store(hash, "vvec", 4, newRV_noinc((SV*)pt = newAV()), 0);
	for(i=0;i<2;i++) {
		av_push(pt, newSVnv(dwg->aden->image.vvec[i]));
	}
	// check for clipping
	if(dwg->aden->image.clipping == 1) {
		hv_store(hash, "clipping", 8, newRV_noinc((SV*)cpts = newAV()), 0);
		num = dwg->aden->image.numclipverts;
		if(num == 2) {
			av_push(cpts, newRV_noinc((SV*)pt = newAV()));
			av_push(pt, newSVnv(dwg->aden->image.rectclipvert0[0]));
			av_push(pt, newSVnv(dwg->aden->image.rectclipvert0[1]));
			av_push(cpts, newRV_noinc((SV*)pt = newAV()));
			av_push(pt, newSVnv(dwg->aden->image.rectclipvert1[0]));
			av_push(pt, newSVnv(dwg->aden->image.rectclipvert1[1]));
		}
		else {
			bcptr=adStartBlobRead(dwg->aden->image.polyclipvertblob);
			for( il = 0; il < num; il++) {
				adReadBlob2Double(bcptr,tempdouble);
				av_push(cpts, newRV_noinc((SV*)pt = newAV()));
				av_push(pt, newSVnv(tempdouble[0]));
				av_push(pt, newSVnv(tempdouble[1]));
			}
		}
	} // end if clipping
	adSeekObject(dwg->handle, dwg->aden->image.imagedefobjhandle, &adobhd, &adob);
	len = strlen(adob.imagedef.filepath);
	hv_store(hash, "fullpath", 8, newSVpvn(adob.imagedef.filepath, len), 0);
	return(newRV_noinc((SV*) hash));
} // getImage

int getentinit(SV* obj) {
	AD_OBJHANDLE mspaceblkobjhandle;
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	// FIXME: this is trapped in modelspace land!
	// would need to have a set_space function and then store the handle
	adGetBlockHandle(dwg->handle, mspaceblkobjhandle, AD_MODELSPACE_HANDLE);
	dwg->entitylist = adEntityList(dwg->handle, mspaceblkobjhandle);
	adStartEntityGet(dwg->entitylist); // rewinds the entitylist
	}

void getent(SV* obj) {
	Inline_Stack_Vars;
	AD_LAY layer;
	long len;
	char * type;
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	PAD_ENT_HDR  adenhd = dwg->adenhd;
	Inline_Stack_Reset;
	if (!( adGetEntity(dwg->entitylist, adenhd, dwg->aden) ) ) {
		Inline_Stack_Done;		
		return;
	}
	adSeekLayer(dwg->handle, adenhd->entlayerobjhandle, &layer);
	len = strlen(layer.name);
	Inline_Stack_Push(sv_2mortal(newSVpvn(layer.name, len)));
	Inline_Stack_Push(sv_2mortal(newSViv(adenhd->entcolor)));
	Inline_Stack_Push(sv_2mortal(newSViv(adenhd->enttype)));
	Inline_Stack_Done;
}

SV* get_extrusion(SV* obj) {
	int i;
	AV* ext = newAV();
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	if(! adEntHasExtrusion(dwg->adenhd->entflags)) {
		return(&PL_sv_undef);
	}
	for(i = 0; i < 3; i++) {
		av_push(ext, newSVnv(dwg->adenhd->extrusion[i]));
	}
	return(newRV_noinc((SV*) ext));
}
/*--------------------------------------------------------------------*/

int set_extrusion(DWGstruct* dwg, HV* hash) {
	SV** psv;
	SV** psv2;
	AV* ext;
	SV* val;
	int i;

	//printf("you called set_extrusion\n");
	if(hv_exists(hash, "extrusion", 9)) {
		psv = hv_fetch(hash, "extrusion", 9, 0);
		val = *psv;
		ext = (AV*) SvRV(val);
		//printf("fixme: trying to set extrusion direction\n");
		for(i=0;i<3;i++) {
			if(av_exists(ext, i)) {
				psv2 = av_fetch(ext, i, 0);
				val = *psv2;
				dwg->adenhd->extrusion[i] = SvNV(val);
			}
		}
	// FIXME: now set the fact that it has extrusion
	adSetEntHasExtrusion(dwg->adenhd->entflags);
	}
	return(1);
}

/*-------------------------Constants Lookup---------------------------*/

char* entype(SV* obj, int type) {
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	switch (type) {
		case AD_ENT_LINE:
			return("lines");
		case AD_ENT_POINT:
			return("points");
		case AD_ENT_CIRCLE:
			return("circles");
		case AD_ENT_TEXT:
			return("texts");
		case AD_ENT_ARC:
			return("arcs");
		case AD_ENT_SOLID3D:
			return("solid3d"); // XXX stupid kludge
		case AD_ENT_REGION:
			return("region3d");
		case AD_ENT_BODY:
			return("body3d");
		case AD_ENT_POLYLINE:
			return("polyline");
		case AD_ENT_VERTEX:
			return("vertex");
		case AD_ENT_SEQEND:
			return("sequence_end");
		case AD_ENT_FACE3D:
			return("face3d");
		default:
			if(type == adImageEnttype(dwg->handle)) 
				return("images");
			if(type == adLwplineEnttype(dwg->handle))
				return("plines");
			return("");
		}
	}

/*-------------------------DESTRUCTOR---------------------------------*/

void DESTROY(SV* obj) {
	DWGstruct* dwg = (DWGstruct*) SvIV(SvRV(obj));
	if(dwg->file_is_open) {
		//printf("closing file\n");
		adCloseFile(dwg->handle);
		}
	//printf("freeing memory\n");
	//free(dwg->current_layer);
	free(dwg->adxd);
	free(dwg->adtb);
	free(dwg->aden);
	free(dwg->adenhd);
	// printf("closing kit\n");
	adCloseAd2();
	// printf("freeing struct\n");
	free(dwg);
} // DESTROY

/*--------------------------------------------------------------------*/
// this function was adapted from the OpenDWG toolkit example
// it allocates memory for the pointers stored in the main struct
int allocateadptrs(DWGstruct* dwg) {
	//printf("allocating memory\n");
	if ((dwg->adenhd=(PAD_ENT_HDR)malloc(sizeof(AD_ENT_HDR)))!=NULL) {
		if ((dwg->aden=(PAD_ENT)malloc(sizeof(AD_ENT)))!=NULL) {
			if ((dwg->adtb=(PAD_TB)malloc(sizeof(AD_TB)))!=NULL) {
				if ((dwg->adxd=(PAD_XD)malloc(sizeof(AD_XD)))!=NULL) {
					//printf("success\n");
					return(1);
				}
				free(dwg->adtb);
			}
			free(dwg->aden);
		}
		free(dwg->adenhd);
	}
	return(0);
}