/* pscmGfx - routines for making postScript output seem a
 * lot like 256 color bitmap output. 
 *
 * This file is copyright 2002 Jim Kent, but license is hereby
 * granted for all use - public, private or commercial. */

#include <math.h>
#ifdef MACHTYPE_sparc
#include <ieeefp.h>
int isinf(double x) { return !finite(x) && x==x; }
#endif
#include "common.h"
#include "hash.h"
#include "memgfx.h"
#include "gfxPoly.h"
#include "colHash.h"
#include "psGfx.h"
#include "pscmGfx.h"
#include "gemfont.h"
#include "vGfx.h"
#include "vGfxPrivate.h"

static struct pscmGfx *boxPscm;	 /* Used to keep from drawing the same box again
                                  * and again with no other calls between.  This
				  * ends up cutting down the file size by 5x
				  * in the whole chromosome case of the browser. */

void pscmSetHint(struct pscmGfx *pscm, char *hint, char *value)
/* set a hint */
{
if (!value) return;
if (sameString(value,""))
    {
    hashRemove(pscm->hints, hint);
    }
struct hashEl *el = hashLookup(pscm->hints, hint);
if (el) 
    {
    freeMem(el->val);
    el->val = cloneString(value);
    }
else
    {
    hashAdd(pscm->hints, hint, cloneString(value));
    }
}

char *pscmGetHint(struct pscmGfx *pscm, char *hint)
/* get a hint */
{
return hashOptionalVal(pscm->hints, hint, "");
}

int pscmGetFontPixelHeight(struct pscmGfx *pscm, MgFont *font)
/* How high in pixels is font? */
{
return font_cel_height(font);
}

int pscmGetFontStringWidth(struct pscmGfx *pscm, MgFont *font, char *string)
/* How wide is a string? */
{
return fnstring_width(font, (unsigned char *)string, strlen(string));
}

void pscmSetClip(struct pscmGfx *pscm, int x, int y, int width, int height)
/* Set clipping rectangle. */
{
double x2 = x + width;
double y2 = y + height;
pscm->clipMinX = x;
pscm->clipMinY = y;
pscm->clipMaxX = x2;     /* one beyond actual last pixel */
pscm->clipMaxY = y2;
/* adjust to pixel-centered coordinates */
x2 -= 1;
y2 -= 1;
double x1 = x;
double y1 = y;
/* pad a half-pixel all the way around the box */
x1 -= 0.5;
y1 -= 0.5;
x2 += 0.5;
y2 += 0.5;
psClipRect(pscm->ps, x1, y1, x2-x1, y2-y1);
}

void pscmUnclip(struct pscmGfx *pscm)
/* Set clipping rect to cover full thing. */
{
pscmSetClip(pscm, 0, 0, pscm->ps->userWidth, pscm->ps->userHeight);
}

int pscmFindColorIx(struct pscmGfx *pscm, int r, int g, int b)
/* Returns closest color in color map to rgb values.  If it doesn't
 * already exist in color map and there's room, it will create
 * exact color in map. */
{
return MAKECOLOR_32(r,g,b);
}

struct rgbColor pscmColorIxToRgb(struct pscmGfx *pscm, int colorIx)
/* Return rgb value at color index. */
{
static struct rgbColor rgb;
rgb.r = (colorIx >> 0) & 0xff;
rgb.g = (colorIx >> 8) & 0xff;
rgb.b = (colorIx >> 16) & 0xff;

return rgb;
}

void pscmSetWriteMode(struct pscmGfx *pscm, unsigned int writeMode)
/* Set write mode */
{
pscm->writeMode = writeMode;
}

struct pscmGfx *pscmOpen(int width, int height, char *file)
/* Return new pscmGfx. */
{
struct pscmGfx *pscm;

AllocVar(pscm);
pscm->ps = psOpen(file, width, height, 72.0 * 7.5, 0, 0);
psTranslate(pscm->ps,0.5,0.5);  /* translate all coordinates to pixel centers */
pscm->clipMinX = pscm->clipMinY = 0;
pscm->clipMaxX = width;     
pscm->clipMaxY = height;
pscm->hints = hashNew(6);
return pscm;
}

void pscmClose(struct pscmGfx **pPscm)
/* Finish writing out and free structure. */
{
struct pscmGfx *pscm = *pPscm;
if (pscm != NULL)
    {
    psClose(&pscm->ps);
    colHashFree(&pscm->colorHash);
    freez(pPscm);
    }
}

void pscmSetColor(struct pscmGfx *pscm, Color color)
/* Set current color to Color. */
{
struct rgbColor *col;

struct rgbColor myCol;
col = &myCol;

col->r = (color >> 0) & 0xff;
col->g = (color >> 8) & 0xff;
col->b = (color >> 16) & 0xff;

if (color != pscm->curColor)
    {
    psSetColor(pscm->ps, col->r, col->g, col->b);
    pscm->curColor = color;
    }
}

void pscmBoxToPs(struct pscmGfx *pscm, int x, int y, 
	int width, int height)
/* adjust coordinates for PS */
{
/* Do some clipping here to make the postScript
 * easier to edit in illustrator. */
double x2 = x + width;
double y2 = y + height;

if (x < pscm->clipMinX) x = pscm->clipMinX;
if (y < pscm->clipMinY) y = pscm->clipMinY;
if (x2 > pscm->clipMaxX) x2 = pscm->clipMaxX;
if (y2 > pscm->clipMaxY) y2 = pscm->clipMaxY;

/* adjust to pixel-centered coordinates */
x2 -= 1;
y2 -= 1;
double x1 = x;
double y1 = y;
/* pad a half-pixel all the way around the box */
x1 -= 0.5;
y1 -= 0.5;
x2 += 0.5;
y2 += 0.5;
psDrawBox(pscm->ps, x1, y1, x2-x1, y2-y1);
}

void pscmBox(struct pscmGfx *pscm, int x, int y, 
	int width, int height, int color)
/* Draw a box. */
{
/* When viewing whole chromosomes the browser tends
 * to draw the same little vertical tick over and
 * over again.  This tries to remove the worst of
 * the redundancy anyway. */

static int lx, ly, lw, lh, lc=-1;
if (x != lx || y != ly || width != lw || height != lh || color != lc || 
	pscm != boxPscm)
    {
    pscmSetColor(pscm, color);
    pscmBoxToPs(pscm, x, y, width, height);
    lx = x;
    ly = y;
    lw = width;
    lh = height;
    lc = color;
    boxPscm = pscm;
    }
}

void pscmDot(struct pscmGfx *pscm, int x, int y, int color)
/* Set a dot. */
{
pscmBox(pscm, x, y, 1, 1, color);
}

static void pscmVerticalSmear(struct pscmGfx *pscm,
	int xOff, int yOff, int width, int height, 
	Color *dots, boolean zeroClear)
/* Put a series of one 'pixel' width vertical lines. */
{
int x, i;
struct psGfx *ps = pscm->ps;
Color c;
for (i=0; i<width; ++i)
    {
    x = xOff + i;
    c = dots[i];
    if (c != MG_WHITE || !zeroClear)
	{
	pscmSetColor(pscm, c);
	psDrawBox(ps, x, yOff, 1, height);
	}
    }
}

static void pscmSetFont(struct pscmGfx *pscm, MgFont *font)
/* Set font. */
{
/* For now we basically still get the sizing info from the old
 * gem fonts.   I'm not sure how to get sizing info out of
 * PostScript.  We'll try and arrange it so that the PostScript
 * fonts match the gem fonts more or less. */
void *v = font;
if (v != pscm->curFont)
    {
    if (pscm->fontMethod == 0) 
        psTimesFont(pscm->ps, font->psHeight);
    else
        psSetFont(pscm->ps, pscm->fontName, font->psHeight);

    pscm->curFont = v;
    }
}

void pscmText(struct pscmGfx *pscm, int x, int y, int color, 
	MgFont *font, char *text)
/* Draw a line of text with upper left corner x,y. */
{
pscmSetColor(pscm, color);
pscmSetFont(pscm, font);
psTextAt(pscm->ps, x, y, text);
boxPscm = NULL;
}

void pscmTextRight(struct pscmGfx *pscm, int x, int y, int width, int height,
	int color, MgFont *font, char *text)
/* Draw a line of text right justified in box defined by x/y/width/height */
{
pscmSetColor(pscm, color);
pscmSetFont(pscm, font);
psTextRight(pscm->ps, x, y, width, height, text);
boxPscm = NULL;
}

void pscmTextCentered(struct pscmGfx *pscm, int x, int y, 
	int width, int height, int color, MgFont *font, char *text)
/* Draw a line of text centered in box defined by x/y/width/height */
{
pscmSetColor(pscm, color);
pscmSetFont(pscm, font);
psTextCentered(pscm->ps, x, y, width, height, text);
boxPscm = NULL;
}

void pscmTextInBox(struct pscmGfx *pscm, int x, int y, 
	int width, int height, int color, MgFont *font, char *text)
/* Draw a line of text that fills in box defined by x/y/width/height */
{
pscmSetColor(pscm, color);
pscmSetFont(pscm, font);
psTextInBox(pscm->ps, x, y, width, height, text);
boxPscm = NULL;
}

void pscmFillUnder(struct pscmGfx *pscm, int x1, int y1, int x2, int y2, 
	int bottom, Color color)
/* Draw a 4 sided filled figure that has line x1/y1 to x2/y2 at
 * it's top, a horizontal line at bottom at it's bottom,  and
 * vertical lines from the bottom to y1 on the left and bottom to
 * y2 on the right. */
{
pscmSetColor(pscm, color);
psFillUnder(pscm->ps, x1, y1, x2, y2, bottom);
boxPscm = NULL;
}

void psPolyFindExtremes(struct gfxPoly *poly, 
  int *pMinX, int *pMaxX,  
  int *pMinY, int *pMaxY)
/* find min and max of x and y */
{
struct gfxPoint *p = poly->ptList;
int minX, maxX, minY, maxY;
minX = maxX =  p->x;
minY = maxY =  p->y;
for (;;)
    {
    p = p->next;
    if (p == poly->ptList)
	break;
    if (minX > p->x) minX = p->x;
    if (minY > p->y) minY = p->y;
    if (maxX < p->x) maxX = p->x;
    if (maxY < p->y) maxY = p->y;
    }
*pMinX = minX;
*pMaxX = maxX;
*pMinY = minY;
*pMaxY = maxY;
}

double gfxSlope(struct gfxPoint *q, struct gfxPoint *p)
/* determine slope from two gfxPoints */
{
double dx = p->x - q->x;
double dy = p->y - q->y;
return dy/dx;
}

boolean colinearPoly(struct gfxPoly *poly)
/* determine if points are co-linear */
{
if (poly->ptCount < 2) return TRUE;
struct gfxPoint *q = poly->ptList, *p=q->next;
double m0 = gfxSlope(q,p);
for (;;)
    {
    p = p->next;
    if (p == poly->ptList)
	break;
    double m1 = gfxSlope(q,p);
    if (!(isinf(m0) && isinf(m1)) && (m1 != m0))
	return FALSE;
    }
return TRUE;
}

double fatPixel(double c, double center, double fat)
/* fatten coordinate by scaling */
{
return center+((c-center)*fat);
}

void pscmPolyFatten(struct psPoly *psPoly, 
  int minX, int maxX, int minY, int maxY)
/* Fatten polygon by finding the center and then
 * scaling by 0.5 pixel from the center in all directions.
 * Caller assures dx and dy will NOT be zero. */
{
int dx = maxX - minX;
int dy = maxY - minY;
double centerX = (maxX + minX) / 2.0;
double centerY = (maxY + minY) / 2.0;
double fatX = (dx + 1.0)/dx;
double fatY = (dy + 1.0)/dy;
struct psPoint *p = psPoly->ptList;
for (;;)
    {
    p->x = fatPixel(p->x, centerX, fatX);
    p->y = fatPixel(p->y, centerY, fatY);
    p = p->next;
    if (p == psPoly->ptList)
	break;
    }
}

void findLineParams(double x1, double y1, double x2, double y2, double *m, double *b)
/* Return parameters slope m and y-intercept b of equation for line from x1,y1 to x2,y2, 
 * vertical lines return infinite slope with b = x value instead */
{
double dx = x2 - x1;
double dy = y2 - y1;
*m = dy/dx;
if (dx == 0)
    *b = x1;
else
    *b = y1 - (*m)*x1;
}


double bDeltaForParallelLine(double d, double m)
/* find delta-b value for parallel line at distance d for line slope m */
{
return d * sqrt(1 + m*m); /* do not call with m == infinity */
}

void adjustBForParallelLine(boolean cw, double m, 
double x1, double y1, 
double x2, double y2, 
double *b)
/* adjust the b value for parallel line at distance d for line slope m 
 *  cw if clockwise */
{
if (isinf(m))  /* handle vertical lines */
    {
    if ((cw && (y2 < y1)) || (!cw && (y2 > y1)))
	*b += 1;   /* b holds x-value rather than y-intercept */
    else
	*b -= 1;
    }
else
    {
    /* Y axis is increasing downwards */
    double bDelta = bDeltaForParallelLine(1.0, m);
    if ((cw && (x2 > x1)) || (!cw && (x2 < x1)))
       *b += bDelta; 
    else
       *b -= bDelta;
    }
}

void findLinesIntersection(boolean cw, double m0, double b0, double m1, double b1, 
double px, double py, boolean posDeltaX, boolean posDeltaY,
double *ix, double *iy)
/* find intersection between two lines */
{

/* colinear vert */
if ((isinf(m0) && isinf(m1)) && (b0 == b1)) 
    {
    if (cw ^ posDeltaY)
    	*ix = px+1;
    else
    	*ix = px-1;
    *iy = py; 
    }
/* colinear horiz */
else if (((m0==0)&&(m1==0)) && (b0 == b1)) 
    {
    if (cw ^ posDeltaX)
    	*iy = py+1;
    else
    	*iy = py-1;
    *ix = px; 
    }
/* colinear */
else if ((m0 == m1) && (b0 == b1))  
    {
    /* inner point shifted 1 pixel away from the point,
     *   moving perpendicular to m0 towards the center */

    double dx, dy;
    double m = -1/m0; 
    dx = sqrt(1/(1+m*m));
    dy = m * dx;

    if (!(cw ^ posDeltaX))
	{
	*ix = px + dx;
	*iy = py + dy;
	}
    else
	{
	*ix = px - dx;
	*iy = py - dy;
	}

    }
/* non-colinear */
else
    {
    if (isinf(m0) && isinf(m1))
	{  /* should be handled earlier by colinear vert lines above */
	errAbort("pscmGfx: m0 and m1 both inf, shouldn't get here");
	}
    else if (!isinf(m0) && isinf(m1))
	{
    	*ix = b1;
	*iy = m0*(*ix)+b0;
	}
    else if (isinf(m0) && !isinf(m1))
	{
    	*ix = b0;
	*iy = m1*(*ix)+b1;
	}
    else if (!isinf(m0) && !isinf(m1))
	{
    	*ix = -(b1-b0)/(m1-m0);
	*iy = m0*(*ix)+b0;
	}
    }
}

boolean pscmIsClockwise(struct psPoly *psPoly)
/* determine if polygon points are in clockwise order or not
 * using cross-product sum*/
{
struct psPoint *p = psPoly->ptList,*q;
double x0, y0; 
double x1, y1;
double x2, y2;
double crossProd = 0;
x1 = p->x;
y1 = p->y;
p = p->next;
x2 = p->x;
y2 = p->y;
p = p->next;
q = p;
for (;;)
    {
    x0 = x1;
    y0 = y1;
    x1 = x2;
    y1 = y2;
    x2 = p->x;
    y2 = p->y;
    crossProd += ((x1-x0)*(y2-y1) - (y1-y0)*(x2-x1));
    p = p->next;
    if (p == q) 
	break;
    }
return (crossProd > 0);
}

void pscmPolyTrapStrokeOutline(struct pscmGfx *pscm, struct psPoly *psPoly)
/* Stroke a fattened polygon using trapezoids. 
 * Make each stroke-segment of the poly be made of a 4-sided trapezoidal figure
 * whose inner edge is parallel 1 pixel away and the points are found
 * by finding the intersections of these parallel lines. */
{

struct psPoint *p = psPoly->ptList,*q;
double px0, py0; /* outer points */
double px1, py1;
double m0,b0;
double m1,b1;
double ix0,iy0;  /* inner points */
double ix1,iy1;

boolean cw = pscmIsClockwise(psPoly);

px1 = p->x;
py1 = p->y;
p = p->next;
findLineParams(px1, py1, p->x, p->y, &m1, &b1);
adjustBForParallelLine(cw, m1, px1, py1, p->x, p->y, &b1);
px0 = px1;
py0 = py1;
px1 = p->x;
py1 = p->y;
p = p->next;
m0 = m1;
b0 = b1;
findLineParams(px1, py1, p->x, p->y, &m1, &b1);
adjustBForParallelLine(cw, m1, px1, py1, p->x, p->y, &b1);

findLinesIntersection(cw,m0,b0,m1,b1,px1,py1,
    (px1<px0) ^ (m0 < 0),
    (py1>py0),
    &ix1,&iy1);

px0 = px1;
py0 = py1;
px1 = p->x;
py1 = p->y;
p = p->next;
q = p;
for (;;)
    {

    m0 = m1;
    b0 = b1;
    ix0 = ix1;
    iy0 = iy1;
    findLineParams(px1, py1, p->x, p->y, &m1, &b1);
    adjustBForParallelLine(cw, m1, px1, py1, p->x, p->y, &b1);

    findLinesIntersection(cw,m0,b0,m1,b1,px1,py1,
	(px1<px0) ^ (m0 < 0),
	(py1>py0),
	&ix1,&iy1);

    struct psPoly *inner = psPolyNew();
    psPolyAddPoint(inner,px0, py0);
    psPolyAddPoint(inner,px1, py1);
    psPolyAddPoint(inner,ix1, iy1);
    psPolyAddPoint(inner,ix0, iy0);
    psDrawPoly(pscm->ps, inner, TRUE);
    psPolyFree(&inner);

    px0 = px1;
    py0 = py1;
    px1 = p->x;
    py1 = p->y;
    p = p->next;
    if (p == q) 
	break;
    }
}

void pscmDrawPoly(struct pscmGfx *pscm, struct gfxPoly *poly, Color color, 
	boolean filled)
/* Draw a polygon, possibly filled, in color. */
{
struct gfxPoint *p = poly->ptList;
struct psPoly *psPoly = psPolyNew();
int minX, maxX, minY, maxY;
if (poly->ptCount < 1)  /* nothing to do */
    {
    return;
    }
psPolyFindExtremes(poly, &minX, &maxX, &minY, &maxY);
/*  check for co-linear polygon, render as line instead */
if (colinearPoly(poly)) 
    {
    pscmLine(pscm, minX, minY, maxX, maxY, color);
    return;
    }
pscmSetColor(pscm, color);
/* convert pixel coords to real values */
for (;;)
    {
    psPolyAddPoint(psPoly,p->x, p->y);
    p = p->next;
    if (p == poly->ptList)
	break;
    }
boolean fat = sameString(pscmGetHint(pscm,"fat"),"on");
if (fat)
    pscmPolyFatten(psPoly, minX, maxX, minY, maxY);
if (fat && !filled)
    pscmPolyTrapStrokeOutline(pscm,psPoly);
else
    psDrawPoly(pscm->ps, psPoly, filled);
psPolyFree(&psPoly);
}

void pscmFatLine(struct pscmGfx *pscm, double x1, double y1, double x2, double y2)
/* Draw a line from x1/y1 to x2/y2 by making a filled polygon.
 *  This also avoids some problems with stroke-width variation
 *  from different postscript implementations. */
{
struct psPoly *psPoly = psPolyNew();

double cX = (x1+x2)/2.0;
double cY = (y1+y2)/2.0;
double fX;
double fY;

/* fatten by lengthing line by 0.5 at each end */
fX = fY = 1.0+0.5*sqrt(0.5);  

/* expand the length of the line by a half-pixel on each end */
x1 = fatPixel(x1,cX,fX);
x2 = fatPixel(x2,cX,fX);
y1 = fatPixel(y1,cY,fY);
y2 = fatPixel(y2,cY,fY);

/* calculate 4 corners {h,i,j,k} of the rectangle covered */

double m = (y2 - y1)/(x2 - x1);
m = -1/m;  /* rotate slope 90 degrees */

double ddX = sqrt( (0.5*0.5) / (m*m+1) );
double ddY = m * ddX;

double hX = x1-ddX, hY = y1-ddY;
double iX = x1+ddX, iY = y1+ddY;
double jX = x2+ddX, jY = y2+ddY;
double kX = x2-ddX, kY = y2-ddY;

psPolyAddPoint(psPoly,hX,hY);
psPolyAddPoint(psPoly,iX,iY);
psPolyAddPoint(psPoly,jX,jY);
psPolyAddPoint(psPoly,kX,kY);
psDrawPoly(pscm->ps, psPoly, TRUE);
psPolyFree(&psPoly);

}

void pscmLine(struct pscmGfx *pscm, 
	int x1, int y1, int x2, int y2, int color)
/* Draw a line from one point to another. */
{
pscmSetColor(pscm, color);
boolean fat = sameString(pscmGetHint(pscm,"fat"),"on");
if (fat)
    pscmFatLine(pscm, x1, y1, x2, y2);
else
    psDrawLine(pscm->ps, x1, y1, x2, y2);
boxPscm = NULL;
}

void pscmCircle(struct pscmGfx *pscm, int xCen, int yCen, int rad,
        Color color, boolean filled)
{
pscmSetColor(pscm, color);
psCircle(pscm->ps, (double)xCen, (double)yCen, (double)rad, filled);
}

void pscmEllipse(struct pscmGfx *pscm, int x1, int y1, int x2, int y2, Color color, 
                        int mode, boolean isDashed)
/* Draw an ellipse specified as a rectangle. Args are left-most and top-most points.
 * Optionally draw half-ellipse (top or bottom) */
{
pscmSetColor(pscm, color);
if (isDashed)
    psSetDash(pscm->ps, TRUE);
else
    psSetDash(pscm->ps, FALSE);
int startAngle = 0;
int endAngle = 360;
if (mode == ELLIPSE_TOP)
    endAngle = 180;
else if (mode == ELLIPSE_BOTTOM)
    startAngle = 180;
int yrad = abs(y1 - y2)/2;
int xrad = abs(x1 - x2)/2;
x2 = x2 - xrad;
y1 = y1 - yrad;
psDrawEllipse(pscm->ps, (double)x2, (double)y1, (double)xrad, (double)yrad,
                startAngle, endAngle);
psSetDash(pscm->ps, FALSE);
}

static double bezierQuadraticToCubic(int a, int b)
/* Derive cubic control points from quadratic control point. */
{
return (double)a + (2 * (b - a))/3.0;
}

void pscmCurve(struct pscmGfx *pscm, int x1, int y1, int x2, int y2, int x3, int y3, Color color,
                        boolean isDashed)
/* Draw Bezier curve specified by 3 points (quadratic Bezier).
 * The points are: first (p1) and last (p3), and 1 control point (p2).
 */ 
{
pscmSetColor(pscm, color);
if (isDashed)
    psSetDash(pscm->ps, TRUE);
else
    psSetDash(pscm->ps, FALSE);
// PostScript bezier is cubic -- derive the two control points from the single quadratic control point
double c1x = bezierQuadraticToCubic(x1, x2);
double c1y = bezierQuadraticToCubic(y1, y2);
double c2x = bezierQuadraticToCubic(x3, x2);
double c2y = bezierQuadraticToCubic(y3, y2);
psDrawCurve(pscm->ps, (double)x1, (double)y1, c1x, c1y, c2x, c2y, (double)x3, (double)y3);
psSetDash(pscm->ps, FALSE);
}

void pscmSetFontMethod(struct pscmGfx *pscm, unsigned int method, char *fontName, char *fontFile)
/* Which font drawing method shoud we use. */
{
pscm->fontMethod = method;
pscm->fontName = cloneString(fontName);
}

struct vGfx *vgOpenPostScript(int width, int height, char *fileName)
/* Open up something that will someday be a PostScript file. */
{
struct vGfx *vg = vgHalfInit(width, height);
vg->data = pscmOpen(width, height, fileName);
vg->close = (vg_close)pscmClose;
vg->dot = (vg_dot)pscmDot;
vg->box = (vg_box)pscmBox;
vg->line = (vg_line)pscmLine;
vg->text = (vg_text)pscmText;
vg->textRight = (vg_textRight)pscmTextRight;
vg->textCentered = (vg_textCentered)pscmTextCentered;
vg->textInBox = (vg_textInBox)pscmTextInBox;
vg->findColorIx = (vg_findColorIx)pscmFindColorIx;
vg->colorIxToRgb = (vg_colorIxToRgb)pscmColorIxToRgb;
vg->setClip = (vg_setClip)pscmSetClip;
vg->unclip = (vg_unclip)pscmUnclip;
vg->verticalSmear = (vg_verticalSmear)pscmVerticalSmear;
vg->fillUnder = (vg_fillUnder)pscmFillUnder;
vg->drawPoly = (vg_drawPoly)pscmDrawPoly;
vg->ellipse = (vg_ellipse)pscmEllipse;
vg->circle = (vg_circle)pscmCircle;
vg->curve = (vg_curve)pscmCurve;
vg->setHint = (vg_setHint)pscmSetHint;
vg->getHint = (vg_getHint)pscmGetHint;
vg->getFontPixelHeight = (vg_getFontPixelHeight)pscmGetFontPixelHeight;
vg->getFontStringWidth = (vg_getFontStringWidth)pscmGetFontStringWidth;
vg->setWriteMode = (vg_setWriteMode)pscmSetWriteMode;
vg->setFontMethod = (vg_setFontMethod)pscmSetFontMethod;
return vg;
}

