 /*
 * Electric(tm) VLSI Design System
 *
 * File: usredtecg.c
 * User interface technology editor: conversion from technology to library
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

#include "global.h"
#include "egraphics.h"
#include "efunction.h"
#include "tech.h"
#include "tecgen.h"
#include "tecart.h"
#include "usr.h"
#include "drc.h"
#include "usredtec.h"

/* prototypes for local routines */
static NODEINST *us_tecedplacegeom(POLYGON*, NODEPROTO*);
static void us_tecedsetlist(NODEINST*, POLYGON*, INTBIG, INTBIG, INTBIG, INTBIG);

/*
 * convert technology "tech" into a library and return that library.
 * Returns NOLIBRARY on error
 */
LIBRARY *us_tecedmakelibfromtech(TECHNOLOGY *tech)
{
	REGISTER LIBRARY *lib;
	REGISTER char *lay, *dxf, **sequence, **varnames, *fname;
	REGISTER INTBIG i, j, k, total, e, xs, ys, oldlam, *newmap, *mapptr,
		tcon, func, gds, nodexpos, bits,
		height3d, thick3d, lambda, min2x, min2y;
	REGISTER BOOLEAN serp, square, wipes, lockable;
	REGISTER BOOLEAN first;
	INTBIG lx, hx, ly, hy, xpos[4], ypos[4], xsc[4], ysc[4], lxo, hxo, lyo, hyo,
		lxp, hxp, lyp, hyp;
	REGISTER NODEPROTO *np, **nplist, *pnp, **aplist;
	float spires, spicap, spiecap;
	REGISTER VARIABLE *var, *var2, *var3, *var5, *var6, *var7, *var8,
		*var10, *var11, *varred, *vargreen, *varblue;
	REGISTER NODEINST *ni, *oni, *nni;
	REGISTER ARCINST *ai;
	REGISTER PORTPROTO *pp, *opp;
	REGISTER ARCPROTO *ap;
	REGISTER CELL *c;
	REGISTER GRAPHICS *desc;
	static POLYGON *poly = NOPOLYGON;
	REGISTER DRCRULES *rules;
	REGISTER TECH_POLYGON *ll;
	REGISTER TECH_NODES *techn;
	REGISTER TECH_COLORMAP *colmap;

	/* make sure network tool is on */
	if ((net_tool->toolstate&TOOLON) == 0)
	{
		ttyputerr(_("Network tool must be running...turning it on"));
		toolturnon(net_tool, FALSE);
		ttyputerr(_("...now reissue the technology editing command"));
		return(NOLIBRARY);
	}

	/* get polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_tool->cluster);

	lib = newlibrary(tech->techname, tech->techname);
	if (lib == NOLIBRARY)
	{
		ttyputerr(_("Cannot create library %s"), tech->techname);
		return(NOLIBRARY);
	}
	ttyputmsg(_("Created library %s..."), tech->techname);

	/* create the information node */
	np = newnodeproto("factors", lib);
	if (np == NONODEPROTO) return(NOLIBRARY);

	/* modify this technology's lambda value to match the current one */
	oldlam = lib->lambda[tech->techindex];
	lambda = lib->lambda[art_tech->techindex];
	lib->lambda[tech->techindex] = lambda;

	us_tecedmakeinfo(np, lambda, tech->techdescript);

	/* copy any miscellaneous variables and make a list of their names */
	j = 0;
	for(i=0; us_knownvars[i].varname != 0; i++)
	{
		us_knownvars[i].ival = 0;
		var = getval((INTBIG)tech, VTECHNOLOGY, -1, us_knownvars[i].varname);
		if (var == NOVARIABLE) continue;
		us_knownvars[i].ival = 1;
		j++;
		(void)setval((INTBIG)lib, VLIBRARY, us_knownvars[i].varname, var->addr, var->type);
	}
	if (j > 0)
	{
		varnames = (char **)emalloc(j * (sizeof (char *)), el_tempcluster);
		if (varnames == 0) return(NOLIBRARY);
		j = 0;
		for(i=0; us_knownvars[i].varname != 0; i++)
			if (us_knownvars[i].ival != 0) varnames[j++] = us_knownvars[i].varname;
		(void)setval((INTBIG)lib, VLIBRARY, "EDTEC_variable_list", (INTBIG)varnames,
			VSTRING|VISARRAY|(j<<VLENGTHSH));
		efree((char *)varnames);
	}

	/* create the layer node names */
	total = tech->layercount;
	nplist = (NODEPROTO **)emalloc((total * (sizeof (NODEPROTO *))), el_tempcluster);
	if (nplist == 0) return(NOLIBRARY);

	/* create the layer nodes */
	ttyputmsg(_("Creating the layers..."));
	var2 = getval((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, "IO_cif_layer_names");
	var3 = getval((INTBIG)tech, VTECHNOLOGY, VINTEGER|VISARRAY, "IO_gds_layer_numbers");
	var8 = getval((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, "IO_dxf_layer_names");
	var5 = getval((INTBIG)tech, VTECHNOLOGY, VFLOAT|VISARRAY, "SIM_spice_resistance");
	var6 = getval((INTBIG)tech, VTECHNOLOGY, VFLOAT|VISARRAY, "SIM_spice_capacitance");
	var7 = getval((INTBIG)tech, VTECHNOLOGY, VFLOAT|VISARRAY, "SIM_spice_edge_capacitance");
	var10 = getval((INTBIG)tech, VTECHNOLOGY, VINTEGER|VISARRAY, "TECH_layer_3dheight");
	var11 = getval((INTBIG)tech, VTECHNOLOGY, VINTEGER|VISARRAY, "TECH_layer_3dthickness");
	for(i=0; i<total; i++)
	{
		desc = tech->layers[i];
		(void)initinfstr();
		(void)addstringtoinfstr("layer-");
		(void)addstringtoinfstr(layername(tech, i));
		fname = returninfstr();

		/* make sure the layer doesn't exist */
		for(c = lib->firstcell; c != NOCELL; c = c->nextcell)
			if (namesame(fname, c->cellname) == 0)
		{
			ttyputerr(_("Warning: multiple layers named '%s'"), fname);
			break;
		}

		np = newnodeproto(fname, lib);
		if (np == NONODEPROTO) return(NOLIBRARY);
		nplist[i] = np;

		/* compute foreign file formats */
		if (var2 == NOVARIABLE) lay = ""; else
			lay = ((char **)var2->addr)[i];
		if (var3 == NOVARIABLE) gds = -1; else
			gds = ((INTBIG *)var3->addr)[i];
		if (var8 == NOVARIABLE) dxf = ""; else
			dxf = ((char **)var8->addr)[i];

		/* compute the SPICE information */
		if (var5 == NOVARIABLE) spires = 0.0; else
			spires = ((float *)var5->addr)[i];
		if (var6 == NOVARIABLE) spicap = 0.0; else
			spicap = ((float *)var6->addr)[i];
		if (var7 == NOVARIABLE) spiecap = 0.0; else
			spiecap = ((float *)var7->addr)[i];

		/* compute the 3D information */
		if (var10 == NOVARIABLE) height3d = 0; else
			height3d = ((INTBIG *)var10->addr)[i];
		if (var11 == NOVARIABLE) thick3d = 0; else
			thick3d = ((INTBIG *)var11->addr)[i];

		/* build the layer facet */
		us_tecedmakelayer(np, desc->col, desc->raster,
			desc->colstyle&(NATURE|OUTLINEPAT), lay, layerfunction(tech, i),
				us_layerletters(tech, i), dxf, gds, spires, spicap, spiecap,
					height3d, thick3d);
	}

	/* save the layer sequence */
	sequence = (char **)emalloc(total * (sizeof (char *)), el_tempcluster);
	if (sequence == 0) return(NOLIBRARY);
	i = 0;
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		if (namesamen(np->cell->cellname, "layer-", 6) == 0)
			sequence[i++] = &np->cell->cellname[6];
	(void)setval((INTBIG)lib, VLIBRARY, "EDTEC_layersequence", (INTBIG)sequence,
		VSTRING|VISARRAY|(total<<VLENGTHSH));
	efree((char *)sequence);

	/* create the color map information */
	ttyputmsg(_("Adding color map and design rules..."));
	var2 = getval((INTBIG)tech, VTECHNOLOGY, VCHAR|VISARRAY, "USER_color_map");
	varred = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_red_key);
	vargreen = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_green_key);
	varblue = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_blue_key);
	if (varred != NOVARIABLE && vargreen != NOVARIABLE && varblue != NOVARIABLE &&
		var2 != NOVARIABLE)
	{
		newmap = emalloc((256*3*SIZEOFINTBIG), el_tempcluster);
		if (newmap == 0) return(NOLIBRARY);
		mapptr = newmap;
		colmap = (TECH_COLORMAP *)var2->addr;
		for(i=0; i<256; i++)
		{
			*mapptr++ = ((INTBIG *)varred->addr)[i];
			*mapptr++ = ((INTBIG *)vargreen->addr)[i];
			*mapptr++ = ((INTBIG *)varblue->addr)[i];
		}
		for(i=0; i<32; i++)
		{
			newmap[(i<<2)*3]   = colmap[i].red;
			newmap[(i<<2)*3+1] = colmap[i].green;
			newmap[(i<<2)*3+2] = colmap[i].blue;
		}
		(void)setval((INTBIG)lib, VLIBRARY, "EDTEC_colormap", (INTBIG)newmap,
			VINTEGER|VISARRAY|((256*3)<<VLENGTHSH));
		efree((char *)newmap);
	}

	/* create the design rule information */
	rules = dr_allocaterules(total, tech->techname);
	if (rules == NODRCRULES) return(NOLIBRARY);
	for(i=0; i<total; i++)
		(void)allocstring(&rules->layernames[i], layername(tech, i), el_tempcluster);
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VFRACT|VISARRAY, dr_min_widthkey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->numlayers; i++) rules->minwidth[i] = ((INTBIG *)var->addr)[i];
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, dr_min_width_rulekey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->numlayers; i++)
			(void)reallocstring(&rules->minwidthR[i], ((char **)var->addr)[i], el_tempcluster);
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VFRACT|VISARRAY, dr_connected_distanceskey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++) rules->conlist[i] = ((INTBIG *)var->addr)[i];
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, dr_connected_distances_rulekey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++)
			(void)reallocstring(&rules->conlistR[i], ((char **)var->addr)[i], el_tempcluster);
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VFRACT|VISARRAY, dr_unconnected_distanceskey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++) rules->unconlist[i] = ((INTBIG *)var->addr)[i];
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, dr_unconnected_distances_rulekey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++)
			(void)reallocstring(&rules->unconlistR[i], ((char **)var->addr)[i], el_tempcluster);
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VFRACT|VISARRAY, dr_connected_distancesWkey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++) rules->conlistW[i] = ((INTBIG *)var->addr)[i];
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, dr_connected_distancesW_rulekey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++)
			(void)reallocstring(&rules->conlistWR[i], ((char **)var->addr)[i], el_tempcluster);
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VFRACT|VISARRAY, dr_unconnected_distancesWkey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++) rules->unconlistW[i] = ((INTBIG *)var->addr)[i];
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, dr_unconnected_distancesW_rulekey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++)
			(void)reallocstring(&rules->unconlistWR[i], ((char **)var->addr)[i], el_tempcluster);
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VFRACT|VISARRAY, dr_connected_distancesMkey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++) rules->conlistM[i] = ((INTBIG *)var->addr)[i];
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, dr_connected_distancesM_rulekey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++)
			(void)reallocstring(&rules->conlistMR[i], ((char **)var->addr)[i], el_tempcluster);
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VFRACT|VISARRAY, dr_unconnected_distancesMkey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++) rules->unconlistM[i] = ((INTBIG *)var->addr)[i];
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, dr_unconnected_distancesM_rulekey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++)
			(void)reallocstring(&rules->unconlistMR[i], ((char **)var->addr)[i], el_tempcluster);
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VFRACT|VISARRAY, dr_edge_distanceskey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++) rules->edgelist[i] = ((INTBIG *)var->addr)[i];
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, dr_edge_distances_rulekey);
	if (var != NOVARIABLE)
		for(i=0; i<rules->utsize; i++)
			(void)reallocstring(&rules->edgelistR[i], ((char **)var->addr)[i], el_tempcluster);
	var = getvalkey((INTBIG)tech, VTECHNOLOGY, VFRACT, dr_wide_limitkey);
	if (var != NOVARIABLE) rules->widelimit = var->addr;

	us_tecedloaddrcmessage(rules, lib);
	dr_freerules(rules);

	/* create the arc nodes */
	ttyputmsg(_("Creating the arcs..."));
	total = 0;
	for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
	{
		(void)initinfstr();
		(void)addstringtoinfstr("arc-");
		(void)addstringtoinfstr(ap->protoname);
		fname = returninfstr();

		/* make sure the arc doesn't exist */
		for(c = lib->firstcell; c != NOCELL; c = c->nextcell)
			if (namesame(fname, c->cellname) == 0)
		{
			ttyputerr(_("Warning: multiple arcs named '%s'"), fname);
			break;
		}

		np = newnodeproto(fname, lib);
		if (np == NONODEPROTO) return(NOLIBRARY);
		ap->temp1 = (INTBIG)np;
		var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, us_arcstylekey);
		if (var != NOVARIABLE) bits = var->addr; else
			bits = ap->userbits;
		us_tecedmakearc(np, (ap->userbits&AFUNCTION)>>AFUNCTIONSH,
			bits&WANTFIXANG, ap->userbits&CANWIPE, bits&WANTNOEXTEND,
				(ap->userbits&AANGLEINC)>>AANGLEINCSH);

		/* now create the arc layers */
		ai = dummyarc();
		ai->proto = ap;
		ai->width = defaultarcwidth(ap);
		ai->end[0].xpos = -90000 * oldlam / lambda;
		ai->end[0].ypos = -10000 * oldlam / lambda;
		ai->end[1].xpos = -60000 * oldlam / lambda;
		ai->end[1].ypos = -10000 * oldlam / lambda;
		ai->length = computedistance(ai->end[0].xpos, ai->end[0].ypos, ai->end[1].xpos, ai->end[1].ypos);
		j = arcpolys(ai, NOWINDOWPART);
		for(i=0; i<j; i++)
		{
			shapearcpoly(ai, i, poly);
			if (poly->layer < 0) continue;
			desc = tech->layers[poly->layer];
			if (desc->bits == LAYERN) continue;

			/* scale the arc geometry appropriately */
			for(k=0; k<poly->count; k++)
			{
				poly->xv[k] = poly->xv[k] * lambda / oldlam;
				poly->yv[k] = poly->yv[k] * lambda / oldlam;
			}

			/* create the node to describe this layer */
			ni = us_tecedplacegeom(poly, np);
			if (ni == NONODEINST) continue;

			/* get graphics for this layer */
			us_teceditsetpatch(ni, desc);
			(void)setval((INTBIG)ni, VNODEINST, "EDTEC_layer", (INTBIG)nplist[poly->layer], VNODEPROTO);
			(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERPATCH, VINTEGER);
		}
		i = (defaultarcwidth(ap) - arcwidthoffset(ai)) / 2 * lambda/oldlam;
		ni = newnodeinst(art_boxprim, -90000-i, -60000+i, -10000-i, -10000+i, 0, 0, np);
		if (ni == NONODEINST) return(NOLIBRARY);
		(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, HIGHLIT, VINTEGER);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_layer", (INTBIG)NONODEPROTO, VNODEPROTO);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERPATCH, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
		total++;
	}

	/* save the arc sequence */
	sequence = (char **)emalloc(total * (sizeof (char *)), el_tempcluster);
	if (sequence == 0) return(NOLIBRARY);
	i = 0;
	for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
		sequence[i++] = &((NODEPROTO *)ap->temp1)->cell->cellname[4];
	(void)setval((INTBIG)lib, VLIBRARY, "EDTEC_arcsequence", (INTBIG)sequence,
		VSTRING|VISARRAY|(total<<VLENGTHSH));
	efree((char *)sequence);

	/* create the node nodes */
	ttyputmsg(_("Creating the nodes..."));
	total = 0;
	for(pnp = tech->firstnodeproto; pnp != NONODEPROTO; pnp = pnp->nextnodeproto)
	{
		pnp->temp1 = 0;
		first = TRUE;

		/* create the node layers */
		oni = dummynode();
		oni->proto = pnp;
		xs = (pnp->highx - pnp->lowx) * 2;
		ys = (pnp->highy - pnp->lowy) * 2;
		if (xs < 5000) xs = 5000;
		if (ys < 5000) ys = 5000;
		nodexpos = -xs*2;
		xpos[0] = nodexpos - xs;
		xpos[1] = nodexpos + xs;
		xpos[2] = nodexpos - xs;
		xpos[3] = nodexpos + xs;
		ypos[0] = -10000 + ys;
		ypos[1] = -10000 + ys;
		ypos[2] = -10000 - ys;
		ypos[3] = -10000 - ys;
		nodesizeoffset(oni, &lxp, &lyp, &hxp, &hyp);
		xs = (pnp->highx - pnp->lowx) - lxp - hxp;
		ys = (pnp->highy - pnp->lowy) - lyp - hyp;
		xsc[0] = xs*1;   ysc[0] = ys*1;
		xsc[1] = xs*2;   ysc[1] = ys*1;
		xsc[2] = xs*1;   ysc[2] = ys*2;
		xsc[3] = xs*2;   ysc[3] = ys*2;

		/* for multicut contacts, make large size be just right for 2 cuts */
		techn = tech->nodeprotos[pnp->primindex-1];
		if (techn->special == MULTICUT)
		{
			min2x = (techn->f1*2 + techn->f3*2 + techn->f4) * oldlam / WHOLE;
			min2y = (techn->f2*2 + techn->f3*2 + techn->f4) * oldlam / WHOLE;
			xsc[1] = min2x;
			xsc[3] = min2x;
			ysc[2] = min2y;
			ysc[3] = min2y;
		}
		for(e=0; e<4; e++)
		{
			/* do not create node if main example had no polygons */
			if (e != 0 && first) continue;

			/* square nodes have only two examples */
			if ((pnp->userbits&NSQUARE) != 0 && (e == 1 || e == 2)) continue;
			oni->lowx  = xpos[e] - xsc[e]/2 - lxp;
			oni->lowy  = ypos[e] - ysc[e]/2 - lyp;
			oni->highx = xpos[e] + xsc[e]/2 + hxp;
			oni->highy = ypos[e] + ysc[e]/2 + hyp;

			/* place the layers */
			j = nodepolys(oni, 0, NOWINDOWPART);
			for(i=0; i<j; i++)
			{
				shapenodepoly(oni, i, poly);
				if (poly->layer < 0) continue;
				desc = tech->layers[poly->layer];
				if (desc->bits == LAYERN) continue;

				for(k=0; k<poly->count; k++)
				{
					poly->xv[k] = poly->xv[k] * lambda / oldlam;
					poly->yv[k] = poly->yv[k] * lambda / oldlam;
				}

				/* create the node facet on the first valid layer */
				if (first)
				{
					first = FALSE;
					(void)initinfstr();
					(void)addstringtoinfstr("node-");
					(void)addstringtoinfstr(pnp->primname);
					fname = returninfstr();

					/* make sure the node doesn't exist */
					for(c = lib->firstcell; c != NOCELL; c = c->nextcell)
						if (namesame(fname, c->cellname) == 0)
					{
						ttyputerr(_("Warning: multiple nodes named '%s'"), fname);
						break;
					}

					np = newnodeproto(fname, lib);
					if (np == NONODEPROTO) return(NOLIBRARY);
					func = (pnp->userbits&NFUNCTION)>>NFUNCTIONSH;
					serp = FALSE;
					if ((func == NPTRANMOS || func == NPTRAPMOS || func == NPTRADMOS) &&
						(pnp->userbits&HOLDSTRACE) != 0)
							serp = TRUE;
					if ((pnp->userbits&NSQUARE) != 0) square = TRUE; else
						square = FALSE;
					if ((pnp->userbits&WIPEON1OR2) != 0) wipes = TRUE; else
						wipes = FALSE;
					if ((pnp->userbits&LOCKEDPRIM) != 0) lockable = TRUE; else
						lockable = FALSE;
					us_tecedmakenode(np, func, serp, square, wipes, lockable);
					total++;
					pnp->temp1 = (INTBIG)np;
				}

				/* create the node to describe this layer */
				ni = us_tecedplacegeom(poly, np);
				if (ni == NONODEINST) return(NOLIBRARY);

				/* get graphics for this layer */
				us_teceditsetpatch(ni, desc);
				(void)setval((INTBIG)ni, VNODEINST, "EDTEC_layer",
					(INTBIG)nplist[poly->layer], VNODEPROTO);
				(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERPATCH, VINTEGER);

				/* set minimum polygon factor on smallest example */
				if (e != 0) continue;
				if (i >= tech->nodeprotos[pnp->primindex-1]->layercount) continue;
				ll = tech->nodeprotos[pnp->primindex-1]->layerlist;
				if (ll == 0) continue;
				if (ll[i].representation != MINBOX) continue;
				var = setval((INTBIG)ni, VNODEINST, "EDTEC_minbox", (INTBIG)"MIN", VSTRING|VDISPLAY);
				if (var != NOVARIABLE)
					defaulttextsize(3, var->textdescript);
			}
			if (first) continue;

			/* create the highlight node */
			lx = (xpos[e]-xsc[e]/2) * lambda / oldlam;
			hx = (xpos[e]+xsc[e]/2) * lambda / oldlam;
			ly = (ypos[e]-ysc[e]/2) * lambda / oldlam;
			hy = (ypos[e]+ysc[e]/2) * lambda / oldlam;
			ni = newnodeinst(art_boxprim, lx, hx, ly, hy, 0, 0, np);
			if (ni == NONODEINST) return(NOLIBRARY);
			(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, HIGHLIT, VINTEGER);
			(void)setval((INTBIG)ni, VNODEINST, "EDTEC_layer", (INTBIG)NONODEPROTO, VNODEPROTO);
			(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERPATCH, VINTEGER);
			endobjectchange((INTBIG)ni, VNODEINST);

			/* create a grab node (only in main example) */
			if (e == 0)
			{
				var = getvalkey((INTBIG)pnp, VNODEPROTO, VINTEGER|VISARRAY, el_prototype_center_key);
				if (var != NOVARIABLE)
				{
					lx = hx = xpos[0] + ((INTBIG *)var->addr)[0];
					ly = hy = ypos[0] + ((INTBIG *)var->addr)[1];
					lx = lx * lambda / oldlam;
					hx = hx * lambda / oldlam;
					ly = ly * lambda / oldlam;
					hy = hy * lambda / oldlam;
					nodeprotosizeoffset(gen_facetcenterprim, &lxo, &lyo, &hxo, &hyo);
					ni = newnodeinst(gen_facetcenterprim, lx-lxo, hx+hxo, ly-lyo, hy+hyo, 0, 0, np);
					if (ni == NONODEINST) return(NOLIBRARY);
					endobjectchange((INTBIG)ni, VNODEINST);
				}
			}

			/* also draw ports */
			for(pp = pnp->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			{
				shapeportpoly(oni, pp, poly, FALSE);
				getbbox(poly, &lx, &hx, &ly, &hy);
				lx = lx * lambda / oldlam;
				hx = hx * lambda / oldlam;
				ly = ly * lambda / oldlam;
				hy = hy * lambda / oldlam;
				nodeprotosizeoffset(gen_portprim, &lxo, &lyo, &hxo, &hyo);
				ni = newnodeinst(gen_portprim, lx-lxo, hx+hxo, ly-lyo, hy+hyo, 0, 0, np);
				if (ni == NONODEINST) return(NOLIBRARY);
				pp->temp1 = (INTBIG)ni;
				(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERPATCH, VINTEGER);
				var = setval((INTBIG)ni, VNODEINST, "EDTEC_portname", (INTBIG)pp->protoname,
					VSTRING|VDISPLAY);
				if (var != NOVARIABLE)
					defaulttextsize(3, var->textdescript);
				endobjectchange((INTBIG)ni, VNODEINST);

				/* on the first sample, also show angle and connection */
				if (e != 0) continue;
				if (((pp->userbits&PORTANGLE)>>PORTANGLESH) != 0 ||
					((pp->userbits&PORTARANGE)>>PORTARANGESH) != 180)
				{
					(void)setval((INTBIG)ni, VNODEINST, "EDTEC_portangle",
						(pp->userbits&PORTANGLE)>>PORTANGLESH, VINTEGER);
					(void)setval((INTBIG)ni, VNODEINST, "EDTEC_portrange",
						(pp->userbits&PORTARANGE)>>PORTARANGESH, VINTEGER);
				}

				/* add in the "local" port connections (from this tech) */
				for(tcon=i=0; pp->connects[i] != NOARCPROTO; i++)
					if (pp->connects[i]->tech == tech) tcon++;
				if (tcon != 0)
				{
					aplist = (NODEPROTO **)emalloc((tcon * (sizeof (NODEPROTO *))), el_tempcluster);
					if (aplist == 0) return(NOLIBRARY);
					for(j=i=0; pp->connects[i] != NOARCPROTO; i++)
						if (pp->connects[i]->tech == tech)
							aplist[j++] = (NODEPROTO *)pp->connects[i]->temp1;
					(void)setval((INTBIG)ni, VNODEINST, "EDTEC_connects",
						(INTBIG)aplist, VNODEPROTO|VISARRAY|(tcon<<VLENGTHSH));
					efree((char *)aplist);
				}

				/* connect the connected ports */
				for(opp = pnp->firstportproto; opp != pp; opp = opp->nextportproto)
				{
					if (opp->network != pp->network) continue;
					nni = (NODEINST *)opp->temp1;
					if (nni == NONODEINST) continue;
					if (newarcinst(gen_universalarc, 0, 0, ni, ni->proto->firstportproto,
						(ni->highx+ni->lowx)/2, (ni->highy+ni->lowy)/2, nni,
							nni->proto->firstportproto, (nni->highx+nni->lowx)/2,
								(nni->highy+nni->lowy)/2, np) == NOARCINST) return(NOLIBRARY);
					break;
				}
			}
		}
	}

	/* save the node sequence */
	sequence = (char **)emalloc(total * (sizeof (char *)), el_tempcluster);
	if (sequence == 0) return(NOLIBRARY);
	i = 0;
	for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		if (np->temp1 != 0)
			sequence[i++] = &((NODEPROTO *)np->temp1)->cell->cellname[5];
	(void)setval((INTBIG)lib, VLIBRARY, "EDTEC_nodesequence", (INTBIG)sequence,
		VSTRING|VISARRAY|(total<<VLENGTHSH));
	efree((char *)sequence);

	/* clean up */
	ttyputmsg(_("Done."));
	efree((char *)nplist);
	lib->lambda[tech->techindex] = oldlam;
	return(lib);
}

/*************************** FACET CREATION HELPERS ***************************/

/*
 * routine to build the appropriate descriptive information for a layer into
 * facet "np".  The color is "colorindex"; the stipple array is in "stip"; the
 * layer style is in "style", the CIF layer is in "ciflayer"; the function is
 * in "functionindex"; the layer letters are in "layerletters"; the DXF layer name(s)
 * are "dxf"; the Calma GDS-II layer is in "gds"; the SPICE resistance is in "spires",
 * the SPICE capacitance is in "spicap", the SPICE edge capacitance is in "spiecap",
 * the 3D height is in "height3d", and the 3D thickness is in "thick3d".
 */
void us_tecedmakelayer(NODEPROTO *np, INTBIG colorindex, UINTSML stip[8], INTBIG style,
	char *ciflayer, INTBIG functionindex, char *layerletters, char *dxf,
	INTBIG gds, float spires, float spicap, float spiecap,
	INTBIG height3d, INTBIG thick3d)
{
	REGISTER NODEINST *nicolor, *ni, *laycolor, *laystipple, *laystyle, *laycif,
		*laydxf, *laycalma, *layfunction, *layletters, *layspires, *layspicap,
		*layspiecap, *lay3dheight, *lay3dthick;
	REGISTER INTBIG i, x, y;
	REGISTER VARIABLE *var;
	char line[20], *colorname, *colorsymbol;
	UINTBIG pattern[8];
	UINTSML spattern[8];

	laycolor = laystipple = laystyle = laycif = laydxf = laycalma = NONODEINST;
	layfunction = layletters = layspires = layspicap = layspiecap = NONODEINST;
	lay3dheight = lay3dthick = NONODEINST;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		var = getval((INTBIG)ni, VNODEINST, VINTEGER, "EDTEC_option");
		if (var == NOVARIABLE) continue;
		switch (var->addr)
		{
			case LAYERCOLOR:     laycolor = ni;      break;
			case LAYERPATTERN:   laystipple = ni;    break;
			case LAYERSTYLE:     laystyle = ni;      break;
			case LAYERCIF:       laycif = ni;        break;
			case LAYERDXF:       laydxf = ni;        break;
			case LAYERGDS:       laycalma = ni;      break;
			case LAYERFUNCTION:  layfunction = ni;   break;
			case LAYERLETTERS:   layletters = ni;    break;
			case LAYERSPIRES:    layspires = ni;     break;
			case LAYERSPICAP:    layspicap = ni;     break;
			case LAYERSPIECAP:   layspiecap = ni;    break;
			case LAYER3DHEIGHT:  lay3dheight = ni;   break;
			case LAYER3DTHICK:   lay3dthick = ni;    break;
		}
	}

	/* create the color information if it is not there */
	if (laycolor == NONODEINST)
	{
		/* create the graphic color object */
		nicolor = newnodeinst(art_filledboxprim, -20000, -10000, 10000, 20000, 0, 0, np);
		if (nicolor == NONODEINST) return;
		(void)setvalkey((INTBIG)nicolor, VNODEINST, art_colorkey, colorindex, VINTEGER);
		(void)setval((INTBIG)np, VNODEPROTO, "EDTEC_colornode", (INTBIG)nicolor, VNODEINST);
		if ((style&NATURE) == PATTERNED)
		{
			if ((style&OUTLINEPAT) == 0)
			{
				for(i=0; i<8; i++) pattern[i] = stip[i];
				(void)setvalkey((INTBIG)nicolor, VNODEINST, art_patternkey, (INTBIG)pattern,
					VINTEGER|VISARRAY|(8<<VLENGTHSH));
			} else
			{
				for(i=0; i<8; i++) spattern[i] = stip[i];
				(void)setvalkey((INTBIG)nicolor, VNODEINST, art_patternkey, (INTBIG)spattern,
					VSHORT|VISARRAY|(8<<VLENGTHSH));
			}
		}
		endobjectchange((INTBIG)nicolor, VNODEINST);

		/* create the text color object */
		ni = newnodeinst(gen_invispinprim, 20000, 20000, 24000, 24000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXTCOLOR);
		if (ecolorname(colorindex, &colorname, &colorsymbol)) colorname = "unknown";
		(void)addstringtoinfstr(colorname);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERCOLOR, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the stipple pattern objects if it is not there */
	if (laystipple == NONODEINST)
	{
		for(x=0; x<16; x++) for(y=0; y<8; y++)
		{
			if ((stip[y] & (1 << (15-x))) != 0)
			{
				ni = newnodeinst(art_filledboxprim, x*2000-40000, x*2000-38000,
					4000-y*2000, 6000-y*2000, 0, 0, np);
			} else
			{
				ni = newnodeinst(art_filledboxprim, x*2000-40000, x*2000-38000, 4000-y*2000,
					6000-y*2000, 0, 0, np);
				for(i=0; i<8; i++) spattern[i] = 0;
				(void)setvalkey((INTBIG)ni, VNODEINST, art_patternkey, (INTBIG)spattern,
					VSHORT|VISARRAY|(8<<VLENGTHSH));
			}
			if (ni == NONODEINST) return;
			(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERPATTERN, VINTEGER);
			endobjectchange((INTBIG)ni, VNODEINST);
		}
		ni = newnodeinst(gen_invispinprim, -24000, -24000, 7000, 7000, 0, 0, np);
		if (ni == NONODEINST) return;
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)"Stipple Pattern",
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			TDSETSIZE(var->textdescript, TXTSETQLAMBDA(2));
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the color style object */
	if (laystyle == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, 18000, 18000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXTSTYLE);
		if ((style&NATURE) == SOLIDC) (void)addstringtoinfstr("solid"); else
		{
			if ((style&OUTLINEPAT) != 0) (void)addstringtoinfstr("patterned/outlined"); else
				(void)addstringtoinfstr("patterned");
		}
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERSTYLE, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the CIF layer object */
	if (laycif == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, 12000, 12000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXTCIF);
		(void)addstringtoinfstr(ciflayer);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERCIF, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the DXF layer object */
	if (laydxf == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, 6000, 6000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXTDXF);
		(void)addstringtoinfstr(dxf);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERDXF, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the Calma GDS-II layer object */
	if (laycalma == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, 0, 0, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXTGDS);
		(void)sprintf(line, "%ld", gds);
		(void)addstringtoinfstr(line);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERGDS, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the Function object */
	if (layfunction == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -6000, -6000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXTFUNCTION);
		us_tecedaddfunstring(functionindex);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERFUNCTION, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the Layer Letters object */
	if (layletters == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -12000, -12000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXTLETTERS);
		(void)addstringtoinfstr(layerletters);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERLETTERS, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the SPICE resistance object */
	if (layspires == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -18000, -18000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXTSPICERES);
		(void)sprintf(line, "%g", spires);
		(void)addstringtoinfstr(line);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERSPIRES, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the SPICE capacitance object */
	if (layspicap == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -24000, -24000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXTSPICECAP);
		(void)sprintf(line, "%g", spicap);
		(void)addstringtoinfstr(line);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERSPICAP, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the SPICE edge capacitance object */
	if (layspiecap == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -30000, -30000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXTSPICEECAP);
		(void)sprintf(line, "%g", spiecap);
		(void)addstringtoinfstr(line);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERSPIECAP, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the 3D height object */
	if (lay3dheight == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -36000, -36000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXT3DHEIGHT);
		(void)sprintf(line, "%ld", height3d);
		(void)addstringtoinfstr(line);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYER3DHEIGHT, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the 3D thickness object */
	if (lay3dthick == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -42000, -42000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXT3DTHICK);
		(void)sprintf(line, "%ld", thick3d);
		(void)addstringtoinfstr(line);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYER3DTHICK, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}
}

/*
 * routine to add the text corresponding to the layer function in "func"
 * to the current infinite string
 */
void us_tecedaddfunstring(INTBIG func)
{
	REGISTER INTBIG i;

	(void)addstringtoinfstr(us_teclayer_functions[func&LFTYPE].name);
	for(i=0; us_teclayer_functions[i].name != 0; i++)
	{
		if (us_teclayer_functions[i].value <= LFTYPE) continue;
		if ((func&us_teclayer_functions[i].value) == 0) continue;
		func &= ~us_teclayer_functions[i].value;
		(void)addtoinfstr(',');
		(void)addstringtoinfstr(us_teclayer_functions[i].name);
	}
}

/*
 * routine to build the appropriate descriptive information for an arc into
 * facet "np".  The function is in "func"; the arc is fixed-angle if "fixang"
 * is nonzero; the arc wipes pins if "wipes" is nonzero; and the arc does
 * not extend its ends if "noextend" is nonzero.  The angle increment is
 * in "anginc".
 */
void us_tecedmakearc(NODEPROTO *np, INTBIG func, INTBIG fixang, INTBIG wipes, INTBIG noextend,
	INTBIG anginc)
{
	REGISTER NODEINST *ni;
	REGISTER VARIABLE *var;
	REGISTER char *str;
	char line[50];

	/* create the Function object */
	ni = newnodeinst(gen_invispinprim, 20000, 20000, 0, 0, 0, 0, np);
	if (ni == NONODEINST) return;
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTFUNCTION);
	(void)addstringtoinfstr(us_tecarc_functions[func].name);
	var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(), VSTRING|VDISPLAY);
	if (var != NOVARIABLE)
		defaulttextsize(2, var->textdescript);
	(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", ARCFUNCTION, VINTEGER);
	endobjectchange((INTBIG)ni, VNODEINST);

	/* create the Fixed-angle object */
	ni = newnodeinst(gen_invispinprim, 20000, 20000, -6000, -6000, 0, 0, np);
	if (ni == NONODEINST) return;
	if (fixang != 0) str = "Fixed-angle: Yes"; else str = "Fixed-angle: No";
	var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)str, VSTRING|VDISPLAY);
	if (var != NOVARIABLE)
		defaulttextsize(2, var->textdescript);
	(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", ARCFIXANG, VINTEGER);
	endobjectchange((INTBIG)ni, VNODEINST);

	/* create the Wipe object */
	ni = newnodeinst(gen_invispinprim, 20000, 20000, -12000, -12000, 0, 0, np);
	if (ni == NONODEINST) return;
	if (wipes != 0) str = "Wipes pins: Yes"; else str = "Wipes pins: No";
	var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)str, VSTRING|VDISPLAY);
	if (var != NOVARIABLE)
		defaulttextsize(2, var->textdescript);
	(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", ARCWIPESPINS, VINTEGER);
	endobjectchange((INTBIG)ni, VNODEINST);

	/* create the Noextend object */
	ni = newnodeinst(gen_invispinprim, 20000, 20000, -18000, -18000, 0, 0, np);
	if (ni == NONODEINST) return;
	if (noextend == 0) str = "Extend arcs: Yes"; else str = "Extend arcs: No";
	var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)str, VSTRING|VDISPLAY);
	if (var != NOVARIABLE)
		defaulttextsize(2, var->textdescript);
	(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", ARCNOEXTEND, VINTEGER);
	endobjectchange((INTBIG)ni, VNODEINST);

	/* create the Angle Increment object */
	ni = newnodeinst(gen_invispinprim, 20000, 20000, -24000, -24000, 0, 0, np);
	if (ni == NONODEINST) return;
	(void)sprintf(line, "Angle increment: %ld", anginc);
	var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)line, VSTRING|VDISPLAY);
	if (var != NOVARIABLE)
		defaulttextsize(2, var->textdescript);
	(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", ARCINC, VINTEGER);
	endobjectchange((INTBIG)ni, VNODEINST);
}

/*
 * routine to build the appropriate descriptive information for a node into
 * facet "np".  The function is in "func", the serpentine transistor factor
 * is in "serp", the node is square if "square" is true, the node
 * is invisible on 1 or 2 arcs if "wipes" is true, and the node is lockable
 * if "lockable" is true.
 */
void us_tecedmakenode(NODEPROTO *np, INTBIG func, BOOLEAN serp, BOOLEAN square, BOOLEAN wipes,
	BOOLEAN lockable)
{
	REGISTER NODEINST *ni, *nifunc, *niserp, *nisquare, *niwipes, *nilockable;
	REGISTER char *str;
	REGISTER VARIABLE *var;

	nifunc = niserp = nisquare = niwipes = nilockable = NONODEINST;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		var = getval((INTBIG)ni, VNODEINST, VINTEGER, "EDTEC_option");
		if (var == NOVARIABLE) continue;
		switch (var->addr)
		{
			case NODEFUNCTION:   nifunc = ni;       break;
			case NODESERPENTINE: niserp = ni;       break;
			case NODESQUARE:     nisquare = ni;     break;
			case NODEWIPES:      niwipes = ni;      break;
			case NODELOCKABLE:   nilockable = ni;   break;
		}
	}

	/* create the Function object if it is not there */
	if (nifunc == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, 0, 0, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr(TECEDNODETEXTFUNCTION);
		(void)addstringtoinfstr(nodefunctionname(func, NONODEINST));
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", NODEFUNCTION, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the Serpentine Transistor object if it is not there */
	if (niserp == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -6000, -6000, 0, 0, np);
		if (ni == NONODEINST) return;
		if (serp) str = "Serpentine transistor: yes"; else
			str = "Serpentine transistor: no";
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)str, VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", NODESERPENTINE, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the Square Node object if it is not there */
	if (nisquare == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -12000, -12000, 0, 0, np);
		if (ni == NONODEINST) return;
		if (square) str = "Square node: yes"; else
			str = "Square node: no";
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)str, VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", NODESQUARE, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the Wipes Node object if it is not there */
	if (niwipes == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -18000, -18000, 0, 0, np);
		if (ni == NONODEINST) return;
		if (wipes) str = "Invisible with 1 or 2 arcs: yes"; else
			str = "Invisible with 1 or 2 arcs: no";
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)str, VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", NODEWIPES, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the Lockable Node object if it is not there */
	if (nilockable == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -24000, -24000, 0, 0, np);
		if (ni == NONODEINST) return;
		if (lockable) str = "Lockable: yes"; else str = "Lockable: no";
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)str, VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", NODELOCKABLE, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}
}

/*
 * routine to build the appropriate descriptive information for the information
 * facet "np".
 */
void us_tecedmakeinfo(NODEPROTO *np, INTBIG lambda, char *description)
{
	REGISTER NODEINST *ni, *nilambda, *nidescript;
	char line[50];
	REGISTER VARIABLE *var;

	nilambda = nidescript = NONODEINST;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		var = getval((INTBIG)ni, VNODEINST, VINTEGER, "EDTEC_option");
		if (var == NOVARIABLE) continue;
		switch (var->addr)
		{
			case TECHLAMBDA:   nilambda = ni;     break;
			case TECHDESCRIPT: nidescript = ni;   break;
		}
	}

	/* create the Lambda object if it is not there */
	if (nilambda == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, 0, 0, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)sprintf(line, "Lambda: %ld", lambda);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)line, VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", TECHLAMBDA, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}

	/* create the Description object if it is not there */
	if (nidescript == NONODEINST)
	{
		ni = newnodeinst(gen_invispinprim, 20000, 20000, -6000, -6000, 0, 0, np);
		if (ni == NONODEINST) return;
		(void)initinfstr();
		(void)addstringtoinfstr("Description: ");
		(void)addstringtoinfstr(description);
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
			VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
			defaulttextsize(2, var->textdescript);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", TECHDESCRIPT, VINTEGER);
		endobjectchange((INTBIG)ni, VNODEINST);
	}
}

NODEINST *us_tecedplacegeom(POLYGON *poly, NODEPROTO *np)
{
	INTBIG lx, hx, ly, hy, dummy;
	REGISTER NODEINST *nni;
	REGISTER VARIABLE *var;

	getbbox(poly, &lx, &hx, &ly, &hy);
	switch (poly->style)
	{
		case FILLED:
			if (!isbox(poly, &dummy, &dummy, &dummy, &dummy))
			{
				nni = newnodeinst(art_filledpolygonprim, lx, hx, ly, hy, 0, 0, np);
				if (nni == NONODEINST) return(NONODEINST);
				us_tecedsetlist(nni, poly, lx, hx, ly, hy);
				endobjectchange((INTBIG)nni, VNODEINST);
				return(nni);
			}
			/* FALLTHROUGH */ 
		case FILLEDRECT:
			nni = newnodeinst(art_filledboxprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case CLOSED:
			if (!isbox(poly, &dummy, &dummy, &dummy, &dummy))
			{
				nni = newnodeinst(art_closedpolygonprim, lx, hx, ly, hy, 0, 0, np);
				if (nni == NONODEINST) return(NONODEINST);
				us_tecedsetlist(nni, poly, lx, hx, ly, hy);
				endobjectchange((INTBIG)nni, VNODEINST);
				return(nni);
			}
			/* FALLTHROUGH */ 
		case CLOSEDRECT:
			nni = newnodeinst(art_boxprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case CROSSED:
			nni = newnodeinst(art_crossedboxprim, lx, hx, ly, hy, 0,0, np);
			if (nni == NONODEINST) return(NONODEINST);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case OPENED:
			nni = newnodeinst(art_openedpolygonprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			us_tecedsetlist(nni, poly, lx, hx, ly, hy);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case OPENEDT1:
			nni = newnodeinst(art_openeddottedpolygonprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			us_tecedsetlist(nni, poly, lx, hx, ly, hy);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case OPENEDT2:
			nni = newnodeinst(art_openeddashedpolygonprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			us_tecedsetlist(nni, poly, lx, hx, ly, hy);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case OPENEDT3:
			nni = newnodeinst(art_openedthickerpolygonprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			us_tecedsetlist(nni, poly, lx, hx, ly, hy);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case CIRCLE:
			nni = newnodeinst(art_circleprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case THICKCIRCLE:
			nni = newnodeinst(art_thickcircleprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case DISC:
			nni = newnodeinst(art_filledcircleprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case CIRCLEARC:
			nni = newnodeinst(art_circleprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			setarcdegrees(nni, 0.0, 45.0*EPI/180.0);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case THICKCIRCLEARC:
			nni = newnodeinst(art_thickcircleprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			setarcdegrees(nni, 0.0, 45.0*EPI/180.0);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case TEXTCENT:
			nni = newnodeinst(gen_invispinprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			var = setvalkey((INTBIG)nni, VNODEINST, art_messagekey,
				(INTBIG)poly->string, VSTRING|VDISPLAY);
			if (var != NOVARIABLE) TDSETPOS(var->textdescript, VTPOSCENT);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case TEXTBOTLEFT:
			nni = newnodeinst(gen_invispinprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			var = setvalkey((INTBIG)nni, VNODEINST, art_messagekey,
				(INTBIG)poly->string, VSTRING|VDISPLAY);
			if (var != NOVARIABLE) TDSETPOS(var->textdescript, VTPOSUPRIGHT);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case TEXTBOTRIGHT:
			nni = newnodeinst(gen_invispinprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			var = setvalkey((INTBIG)nni, VNODEINST, art_messagekey,
				(INTBIG)poly->string, VSTRING|VDISPLAY);
			if (var != NOVARIABLE) TDSETPOS(var->textdescript, VTPOSUPLEFT);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
		case TEXTBOX:
			nni = newnodeinst(gen_invispinprim, lx, hx, ly, hy, 0, 0, np);
			if (nni == NONODEINST) return(NONODEINST);
			var = setvalkey((INTBIG)nni, VNODEINST, art_messagekey,
				(INTBIG)poly->string, VSTRING|VDISPLAY);
			if (var != NOVARIABLE) TDSETPOS(var->textdescript, VTPOSBOXED);
			endobjectchange((INTBIG)nni, VNODEINST);
			return(nni);
	}
	return(NONODEINST);
}

void us_tecedsetlist(NODEINST *ni, POLYGON *poly, INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy)
{
	REGISTER INTBIG *list;
	REGISTER INTBIG i;

	list = emalloc((poly->count*2*SIZEOFINTBIG), el_tempcluster);
	if (list == 0) return;
	for(i=0; i<poly->count; i++)
	{
		list[i*2] = poly->xv[i] - (hx+lx)/2;
		list[i*2+1] = poly->yv[i] - (hy+ly)/2;
	}
	(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)list,
		VINTEGER|VISARRAY|((poly->count*2)<<VLENGTHSH));
	efree((char *)list);
}
