Sindbad~EG File Manager
/* M. Beeson, for Mathpert */
/* This file will supply the SVG functions for drawing graphs.
*/
/*
2.22.24 Original date, roughly modeled after wingraph.c
2.26.24 added get_graphpencolor
2.28.24 put 'ascent' into text()
2.29.24 modified get_device at 'synchronize'
3.1.24 added code to set_svgDevice to set theDevice.xPixelsPerInch and yPixelsPerInch
3.2.34 made set_graphpencolor and set_linewidth call end_path and begin_path if inpath is nonaero.
3.5.24 made x1,x2.. doubles in adjust_graph_ranges, so no need to call ROUND
3.5.24 made set_viewport set g->pxmin etc.
3.5.24 wrote filled_circle
3.14.24 allowed rect == NULL in set_svgDevice
3.17.24 modified text()
3.31.24 corrected set_svgDevice, which had p->ymin and p->ymax backwards.
6.24.24 modified get_graphbackgroundcolor and set_graphbackgroundcolor.
7.9.24 added begin_svgGroup and end_svgGroup
7.11.24 added code to set_svgDevice to set theDevice->ypixelsPerInch
7.14.24 corrected set_svgDevice, which had dmax and dmin switched.
7.29.24 added svg_open_filled_circle and open_filled_circle.
Made all the svg circle functions end in newline;
made all the svg rect and path functions end in newline and not start
with newline.
8.9.24 made text() use 12-point type instead of 10-point to get larger type
on axes labels and ticks.
9.2.24 add world_to_pixel2
10.16.24 modified svg_unstyledrect to code its output for short data
11.26.24 corrected a typo in set_svgDevice in theDevice.dxmin = (r->left)/theDevice.numxpixels;
11.26.24 rewrote get_device_size
3.3.254 gave cliprect a second argument and made it static
*/
#include <stdlib.h> /* calloc and free used in polygon */
#include <stddef.h> /* ptrdiff_t */
#define ROUND(x) (short)((x)+0.5)
#include <string.h>
#include <assert.h>
#include <math.h> // fabs
#include "terms.h"
#include "defns.h"
#include "vaux.h" // parameter
#include "wcolors.h"
#include "display1.h" // ROMAN
#include "deval.h" // BADVAL
#include "display.h" // coord, required by termtoSVG.h
#include "graphstr.h"
#include "svgGraph.h"
#include "termtoSVG.h"
#include "svgSymbolText.h"
static svgDevice theDevice;
static char *next;
static int buffersize;
static int inpath; // set to 1 by begin_path, to 0 by end_path, used for debugging
static void svg_moveto(double x, double y);
static void svg_lineto(double x, double y);
static void svg_filled_circle(double a, double b, double r, unsigned rgb, int visibility);
static void svg_filled_circle2(double a, double b, double r, unsigned rgb, int visibility,double parameterValue);
static void svg_open_circle(double a, double b, double r, unsigned rgbstroke, double linewidth,int visibility);
/*________________________________________________________*/
void svg_colorstring(char colorbuf[32], int rgb)
/* write a string like rgb(20,30,40) into colorbuf
with a null terminator, obtained from rgb.
or, if the fourth byte is not zero, rgba(20,30,40,0.5).
*/
{ int red = rgb & 255;
int green = (int)(((unsigned)(rgb & (255 << 8))) >> 8);
int blue = (int)(((unsigned)(rgb & (255 << 16))) >> 16);
int alpha = (int)(((unsigned)(rgb & (255 << 24))) >> 24);
if(alpha == 0)
snprintf(colorbuf,sizeof(char[32]),"rgb(%d,%d,%d)",red,green,blue);
// note, sizeof(colorbuf) will be sizeof(char *)
else
snprintf(colorbuf,sizeof(char[32]),"rgba(%d,%d,%d,%.2lf)",red,green,blue,alpha/255.0);
}
/*________________________________________________________________________*/
/* return pixel viewport coordinates with (0,0) in upper left */
void world_to_pixel(double x, double y,double *u, double *v)
{ if( x - theDevice.xmin > theDevice.xcliplimit)
x = theDevice.xmin + theDevice.xcliplimit;
if( theDevice.xmin - x >theDevice.xcliplimit)
x = theDevice.xmin -theDevice.xcliplimit;
if( y - theDevice.ymax > theDevice.ycliplimit)
y = theDevice.ymax + theDevice.ycliplimit;
if( theDevice.ymax - y > theDevice.ycliplimit)
y = theDevice.ymax -theDevice.ycliplimit;
*u = theDevice.xfactor * (x - theDevice.xmin);
*v = theDevice.yfactor * (theDevice.ymax - y);
}
/*_________________________________________________________*/
static void world_to_pixel2(double x, double y,double *u, double *v)
/* does not assume (0,0) is in the uppper left; needed in drawLarge
*/
{ double a,b;
world_to_pixel(x, y, &a, &b);
a += theDevice.pxmin;
b += theDevice.pymin;
*u = a;
*v = b;
}
/*_________________________________________________________*/
/* To write something to PHP from another file,
call get_device. When you're done, call relinquish_device.
*/
svgDevice * get_device(void)
/* return (a pointer to) the currently active device */
{
theDevice.next = next; // synchronize theDevice with static global next.
if(*next != '\0')
assert(0);
return &theDevice;
}
void relinquish_device(void)
/* synchronize global next
*/
{ // data may have been written to theDevice.next
next = theDevice.next;
if(*next != '\0')
assert(0);
}
/*_________________________________________________________*/
void set_svgDevice( char *outbuffer,
// start of buffer eventually to send to PHP
int bytesavailable, // size of outbuffer
rect *r /* the rectangle r containing all the graphs,
namely, the browser window excluding the toolbar.
By contrast the "viewport" is a rectangle where the Engine
will draw one axes and one or more graphs
on those axes. It is set by set_viewport. */
)
/* You must call this before drawing the graphs.
The device will remain valid
only as long as the window is not resized or moved. The rectangle
passed in is used to set the pixel size of the device.
rect has fields right, left, top, bottom.
Memory must be pre-allocated for outbuffer. This function
does not allocate any memory, so there is no need for a function
'release_device' (but see relinquish_device above).
There is only one "device", called "theDevice". It maintains
the coordinates of the window and the coordinates of the "viewport".
The viewport is changed by "set_viewport". Each graph has its
own viewport, so you call set_viewport before drawing a graph.
They are drawn in "world coordinates" that are set to the desired
x and y ranges of the graph by set_world. Finally, all coordinates
are convered to CSS pixels for drawing as SVG. Since CSS pixels are
larger than physical pixels, those coordinates are floating-point
numbers.
*/
{
theDevice.outbuffer = outbuffer;
next = outbuffer; // next is a static global
buffersize = bytesavailable; // buffersize is a static global
theDevice.bytesavailable = bytesavailable;
theDevice.numxpixels = r->right -r->left+1; // pixels available in viewport
theDevice.numypixels = r->bottom-r->top+1;
theDevice.linewidth = 3;
theDevice.pxmax = r->right;
theDevice.pxmin = r->left; // which is the toolbar width
theDevice.pymax = r->bottom; // pixel coordinates increase downwards
theDevice.pymin = r->top;
theDevice.dxmax = (r->right)/theDevice.numxpixels;
theDevice.dxmin = (r->left)/theDevice.numxpixels;
// device coordinates are relative to r, which excludes the toolbar
theDevice.dymin = r->top/theDevice.numypixels;
theDevice.dymax = r->bottom/theDevice.numypixels;
theDevice.fontheight = 16;
theDevice.fontwidth = 9;
theDevice.activefont = ROMAN;
theDevice.baseline = 12;
theDevice.lineskip = 6;
theDevice.xPixelsPerInch = 72;
theDevice.yPixelsPerInch = 72;
}
/*___________________________________________________________________________*/
/* input is in normalized device coords with (0,0) at lower left and
both directions 1.0 in size */
void set_viewport(double xmin,double xmax,double ymin,double ymax)
/* The viewport is specified in normalized device coordinates,
which are recorded in the svgDevice structure 'theDevice' (by this
function). Subsequent
calls to set_world will set world coordinates in this viewport,
and all drawing will be clipped to the viewport. */
{
theDevice.dxmin = xmin;
theDevice.dxmax = xmax;
theDevice.dymin = ymin;
theDevice.dymax = ymax;
}
/*___________________________________________________________________________*/
#define MAXPIXELS 0x1fff /* 1 less than 8K */
/* set_world (below) calculates the world coordinates cliplimit
that corresponds to MAXPIXELS pixels. MAXPIXELS should be set
one less than the maximum device coordinates the underlying graphics
system can handle. If you don't worry about this, you might get
spurious lines when there are too-large coordinates near singularities. */
void set_world(double xmin,double xmax,double ymin,double ymax)
/* Set world coordinates in the current device */
{ theDevice.xmin = xmin;
theDevice.xmax = xmax;
theDevice.ymin = ymin;
theDevice.ymax = ymax;
theDevice.xfactor = (theDevice.dxmax -theDevice.dxmin) * (theDevice.numxpixels)/(xmax-xmin); // had -1 in num
theDevice.yfactor = (theDevice.dymax -theDevice.dymin) * (theDevice.numypixels)/(ymax-ymin); // had -1 in num
theDevice.xpixel = (xmax - xmin)/ (double) (theDevice.pxmax - theDevice.pxmin);// + 1);
assert(theDevice.xpixel > 0);
theDevice.ypixel = (ymax - ymin)/ (double)(theDevice.pymax - theDevice.pymin);
// + 1);
if(theDevice.ypixel <= 0)
assert(0);
theDevice.xcliplimit = (double) MAXPIXELS * (theDevice.xmax-theDevice.xmin)/(theDevice.numxpixels);
theDevice.ycliplimit = (double) MAXPIXELS * (theDevice.ymax-theDevice.ymin)/(theDevice.numypixels);
}
/*___________________________________________________________________________*/
static void svg_lineto(double x, double y)
/* emit SVG code to draw line from current location to (x,y); */
/* no specification of color, width, etc. */
{ sprintf(next, "L %.1f %.1f ", x, y);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(buffersize < 1000)
assert(0);
if(!inpath)
assert(0); // debugging
if(*next != '\0')
{ printf("%s\n", next);
assert(0); /* used for debugging */
}
}
/*___________________________________________________________________________*/
static void svg_moveto(double x, double y)
/* emit SVG code to draw line from current location to (x,y); */
{ sprintf(next, "M %.1f %.1f ", x, y);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(!inpath)
assert(0); // debugging
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
void move_to(double x,double y)
/* x and y are world coordinates */
{ double a,b; /* pixel coordinates of x,y */
world_to_pixel(x,y,&a,&b); /* a and b are pixel viewport coordinates */
theDevice.x = x; /* update record of current world-coordinate pen position */
theDevice.y = y;
svg_moveto(a+theDevice.pxmin,b+theDevice.pymin);
}
/*___________________________________________________________________________*/
void line_to(double x,double y)
/* x and y are world coordinates */
/* draw line from current location to (x,y); */
/* In Windows, it checked 'xmode' and used xor-mode when xmode != 0,
but xor-mode isn't a thing in SVG.
*/
{ double a,b; /* pixel coordinates of x,y */
world_to_pixel(x,y,&a,&b); /* a and b are pixel viewport coordinates */
theDevice.x = x; /* update record of current world-coordinate pen position */
theDevice.y = y;
svg_lineto(a+theDevice.pxmin,b+theDevice.pymin);
}
/*___________________________________________________________________________*/
void move_rel(double x,double y)
/* x and y are world coordinates */
{ move_to(theDevice.x + x,theDevice.y + y);
}
/*___________________________________________________________________________*/
void line_rel(double x,double y)
/* x and y are world coordinates */
{ line_to(theDevice.x + x, theDevice.y + y);
}
/*___________________________________________________________________________*/
void begin_path(void) // fill:none
/* print SVG code to start a <path element and leave it ready for a M command
*/
{ char colorbuf[32];
svg_colorstring(colorbuf,get_graphpencolor());
if(inpath)
assert(0);
inpath = 1;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
sprintf(next, "<path stroke=\"%s\" fill=\"none\" stroke-width=\"%0.2lf\" d = \"", colorbuf, get_linewidth());
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
void begin_path2(char *attr)
/* print SVG code to start a <path element and leave it ready for a M command
Supplying also one or more attributes. For example
attr might be
param="3.4" style="display:none;" (semicolon needed there!)
*/
{
char colorbuf[32];
svg_colorstring(colorbuf, get_graphpencolor());
if (inpath)
assert(0);
inpath = 1;
if (*next != '\0') {
printf("%s\n", next);
assert(0);
}
// Use attr directly (no escaping needed)
sprintf(next, "<path %s stroke=\"%s\" fill=\"none\" stroke-width=\"%0.2lf\" d=\"",
attr, colorbuf, get_linewidth());
int byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if (*next != '\0') {
printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
void begin_path3(double pvalue, int visibility)
// pvalue is the value of the active parameter.
// visibility tells whether to make the path have display:block or display:none;
{ char attr[128];
if (fabs(pvalue) < 1e-10) // Tiny threshold
pvalue = 0.0; // avoid outputting "-0.0000"
if(!visibility)
sprintf(attr,"param=%.4f style=\"display:none;\"",pvalue);
// exactly 4 decimal places, which Javascript will match on the client side
else
sprintf(attr,"param=%.4f style=\"display:block;\"",pvalue);
begin_path2(attr);
}
/*___________________________________________________________________________*/
void end_path(void)
// Close the path data attribute and the path element
{ if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
if(inpath == 0)
assert(0);
sprintf( next, "\" />\n");
// the quotes close the data attribute and /> closes the path
int byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
inpath = 0;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*__________________________________________________________________________*/
void begin_svg(char *class, char *id)
/* print SVG code to start an <svg element
*/
{ if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
sprintf(next, "\n<svg class=\"%s\" id=\"%s\" xmlns=\"http://www.w3.org/2000/svg\">\n", class, id);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
void begin_svg_withrect(char *class, char *id, int top, int left, int width, int height)
/* print SVG code to start an <svg element, with more properties specified.
Note that top and left are ignored by SVG, but we can put them there and
then javascript can recover them.
*/
{ if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
sprintf(next, "\n<svg class=\"%s\" id=\"%s\" top = \"%d\" left =\"%d\" width= \"%d\" height = \"%d\" xmlns=\"http://www.w3.org/2000/svg\">\n", class, id, top,left, width, height);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
void begin_svgGroup(char *class, char *id)
{ if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
sprintf(next, "\n<g class=\"%s\" id=\"%s\">", class,id);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
void end_svgGroup(void)
{ if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
sprintf(next, "\n</g>");
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
void begin_svg_withmyrect(char *class, char *id, int top, int left, int width, int height)
/* print SVG code to start an <svg element, with more properties specified.
Note that top and left are ignored by SVG, but we can put them there and
then javascript can recover them. Also note that 'width' and 'height' affect
the way coordinates are interpreted, and we don't want them here, so we use
mywidth and myheight instead.
*/
{ if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
sprintf(next, "\n<svg class=\"%s\" id=\"%s\" top = \"%d\" left =\"%d\" mywidth= \"%d\" myheight = \"%d\" xmlns=\"http://www.w3.org/2000/svg\">", class, id, top,left, width, height);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
static int in_group;
void end_svg(void)
{
if(in_group)
{ sprintf(next,"</g>\n</svg>\n");
in_group = 0;
}
else
sprintf(next, "</svg>\n");
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
void set_cliprect(rect r, int graphnumber)
{
sprintf(next, "\n<defs>\n<clipPath id=\"clipRect%d\"> <rect x=\"%d\" y=\"%d\" width= \"%d\" height=\"%d\"/></clipPath>\n</defs>\n<g clip-path=\"url(#clipRect%d)\">",
graphnumber, r.left, r.top, r.right - r.left, r.bottom - r.top, graphnumber);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
in_group = 1;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
void draw_circle(double x, double y, double r) /* in world coordinates */
/* the radius will be measured in the x-direction */
/* I guess it WILL look circular since aspect is 1 */
/* used in making POLAR_CIRCULAR graphs */
{
open_circle(x,y,r);
}
/*___________________________________________________________________________*/
void filled_circle(double x, double y, double r, int visibility)
/* draw a filled circle at x,y, in world coordinates, of radius r in
horizontal world coordinates, in the current pencolor */
{ if(inpath)
end_path();
svgDevice *theDevice = get_device();
unsigned rgbfill = theDevice->pencolor;
double a,b,radius; /* pixel coordinates */
world_to_pixel(x,y,&a,&b); /* a and b are pixel viewport coordinates */
radius = r / theDevice->xpixel;
a += theDevice->pxmin;
b += theDevice->pymin;
svg_filled_circle(a,b,radius,rgbfill,visibility);
if(inpath)
begin_path();
}
/*___________________________________________________________________________*/
void filled_circle2(double x, double y, double r, int visibility, double parameterValue)
/* draw a filled circle at x,y, in world coordinates, of radius r in
horizontal world coordinates, in the current pencolor and tag its SVG with
param = parameterValue
*/
{ if(inpath)
end_path();
svgDevice *theDevice = get_device();
unsigned rgbfill = theDevice->pencolor;
double a,b,radius; /* pixel coordinates */
world_to_pixel(x,y,&a,&b); /* a and b are pixel viewport coordinates */
radius = r / theDevice->xpixel;
a += theDevice->pxmin;
b += theDevice->pymin;
svg_filled_circle2(a,b,radius,rgbfill,visibility, parameterValue);
if(inpath)
begin_path3(parameterValue,visibility);
}
/*_______________________________________________________________________*/
static void svg_filled_circle(double a, double b, double r, unsigned rgb, int visibility)
// filled circle, no stroke; a,b,r are in CSS pixel coordinates
{
char colorbuf[32];
svg_colorstring(colorbuf,rgb);
char *visibility_string = visibility ? "block": "none";
sprintf(next, "\n<circle cx=\"%lf\" cy=\"%lf\" r=\"%lf\" style =\"fill:%s; stroke:none;display:%s;\"/>\n",a,b,r,colorbuf,visibility_string);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*_______________________________________________________________________*/
static void svg_filled_circle2(double a, double b, double r, unsigned rgb, int visibility,double parameterValue)
// filled circle, no stroke; a,b,r are in CSS pixel coordinates
{
char colorbuf[32];
svg_colorstring(colorbuf,rgb);
char *visibility_string = visibility ? "block": "none";
sprintf(next, "\n<circle cx=\"%lf\" cy=\"%lf\" r=\"%lf\" param=%.4f style =\"fill:%s; stroke:none;display:%s;\"/>\n",a,b,r,parameterValue,colorbuf,visibility_string);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
void open_circle(double x, double y, double r)
/* x,y are world coordinates. Use the
current pencolor for stroke and the current linewidth,
but no fill.
*/
{ double a,b,radius;
world_to_pixel(x,y,&a,&b);
a += theDevice.pxmin;
b += theDevice.pymin;
radius = r / theDevice.xpixel;
double linewidth = theDevice.linewidth;
unsigned stroke = theDevice.pencolor;
svg_open_circle(a,b,radius,stroke,linewidth,1);
}
/*___________________________________________________________________________*/
static void svg_open_circle(double a, double b, double r, unsigned rgbstroke, double linewidth, int visibility)
/* draw an open TRUE circle at x,y, in CSS pixel coordinates, of radius r.
The circle is drawn with the specified stroke and linewidth, but no fill.
*/
{
char colorbufstroke[32];
svg_colorstring(colorbufstroke,rgbstroke);
char *visibility_string = visibility ? "block": "none";
sprintf(next, "\n<circle cx=\"%lf\" cy=\"%lf\" r=\"%lf\" style =\"fill:%s; stroke:%s; stroke-width:%0.2lf;display:%s;\"/>\n",
a,b,r,"none",colorbufstroke,linewidth,visibility_string);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
static void svg_open_filled_circle(double a, double b, double r, unsigned rgbstroke, double linewidth, int fillcolor, int visibility, double parameterValue)
/* draw an open TRUE circle at x,y, in CSS pixel coordinates, of radius r.
The circle is drawn with the specified stroke and fillcolor and linewidth,
and the SVG is tagged with param = parameterValue
*/
{
char colorbufstroke[32];
char colorbuffill[32];
svg_colorstring(colorbufstroke,rgbstroke);
svg_colorstring(colorbuffill,fillcolor);
char *visibility_string = visibility ? "block": "none";
sprintf(next, "\n<circle cx=\"%lf\" cy=\"%lf\" r=\"%lf\" param=\"%.4f\" style =\"fill:%s; stroke:%s; stroke-width:%0.2lf;display:%s;\"/>\n",
a,b,r, parameterValue, colorbuffill,colorbufstroke,linewidth,visibility_string);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
if(*next != '\0')
{ printf("%s\n", next);
assert(0);
}
}
/*___________________________________________________________________________*/
void open_filled_circle(double x, double y, double r, int visibility, double parameterValue)
/* x,y are world coordinates. Use the
current pencolor for stroke and the current linewidth,
and fill with the current graphbackgroundcolor.
*/
{ double a,b,radius;
world_to_pixel(x,y,&a,&b);
a += theDevice.pxmin;
b += theDevice.pymin;
radius = r / theDevice.xpixel;
double linewidth = theDevice.linewidth;
unsigned stroke = theDevice.pencolor;
int fillcolor = theDevice.graphbackgroundcolor;
svg_open_filled_circle(a,b,radius,stroke,linewidth,fillcolor,visibility,parameterValue);
}
/*___________________________________________________________*/
void set_graphpencolor(unsigned int c)
/* Set the current pen color */
/* c is 24 bits of RBG info, 8 bits per color; */
{ if(inpath)
{ end_path();
theDevice.pencolor=c; /* this holds a COLORREF */
begin_path();
}
else
{
theDevice.pencolor=c;
}
}
unsigned get_graphpencolor(void)
/* return the current graphpencolor */
{
return theDevice.pencolor; /* this holds a COLORREF */
}
/*___________________________________________________________*/
void set_graphaxescolor(unsigned int c)
/* Set the current axes color */
/* c is 24 bits of RBG info, 8 bits per color; */
{
theDevice.axescolor=c; /* this holds a COLORREF */
}
unsigned int get_graphaxescolor(void)
{ return theDevice.axescolor;
}
/*___________________________________________________________*/
void set_graphbordercolor(unsigned int c)
/* Set the current pen color */
/* c is 24 bits of RBG info, 8 bits per color; */
{
theDevice.bordercolor=c; /* this holds a COLORREF */
}
unsigned int get_graphbordercolor(void)
{ return theDevice.bordercolor;
}
/*___________________________________________________________________________*/
void set_graphtextcolor(unsigned int x)
/* set the text color */
{ theDevice.textcolor = x;
}
unsigned int get_graphtextcolor(void)
{ return theDevice.textcolor;
}
/*_______________________________________________________________*/
void set_graphbackgroundcolor(unsigned int x)
/* Set the background color */
{
theDevice.graphbackgroundcolor = x;
}
unsigned int get_graphbackgroundcolor(void)
{
return theDevice.graphbackgroundcolor;
}
/*________________________________________________________________*/
void clear_viewport(void)
/* clears viewport to current graphbackground color */
{ unsigned int rgb = get_graphbackgroundcolor();
svg_filledrect(theDevice.pxmin, theDevice.pymin,
theDevice.pxmax, theDevice.pymax,
rgb);
}
/*___________________________________________________________________________*/
void draw_box(double x1,double x2,double y1,double y2)
/* outline a rectangle with current graphpen color and no fill.
x1,x2,y1,y2 are in world coordinates */
{ double a,b,c,d;
unsigned int rgb = get_graphbordercolor();
world_to_pixel(x1,y1,&a,&b);
world_to_pixel(x2,y2,&c,&d);
svg_rect(a,b,c,d,rgb);
}
/*___________________________________________________________________________*/
void set_linewidth(double width)
/* device coordinates--pixels that is--for both screen and printer. */
{
if(width == 0)
assert(0); // never set it to zero!
if(inpath)
{ end_path();
theDevice.linewidth = width;
begin_path();
}
else
{
theDevice.linewidth = width;
}
}
double get_linewidth(void)
{ if (theDevice.linewidth == 0)
assert(0);
return theDevice.linewidth;
}
/*___________________________________________________________________________*/
int draw_pixel(double x, double y, unsigned color)
/* set the pixel at (x,y) in device coordinates to the given color */
/* return 0 for success, 1 for out of buffer space */
{
// svg_filledrect(x-0.5,y-0.5,x+0.5,y+0.5,color);
/* That takes too much space. We therefore encode it here.
It will be decoded to SVG in GetProblem.php, so this coding does not
save bandwidth between webserver and browser, only between Engine and PHP;
but otherwise the six-digit limit on message length will break on graphrelation().
Before using this, call void svg_style_rectangles(color), which will output
CSS code to color rectangles 'color'. Otherwise,
color is supplied hard-coded in GraphDoc.php, which will cause
weird effects if the color for those graphs is changed.
*/
// static int countpixels = 0;
// printf("%d\n",countpixels++); // around 200,000, at 17 bytes each makes 3.4 megabytes
if(buffersize < 100000)
return 1; // and don't draw;
// printf("%d\n",buffersize);
sprintf(next,"<q %d %d>",(int)x,(int)y);
int byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
return 0;
}
/*___________________________________________________________________________*/
void xormode(int flag)
/* if 'flag' is nonzero, turn xormode on, else turn it off */
{
return; // xormode isn't a thing in SVG
}
/*___________________________________________________________________________*/
void text(char *s)
/* Used for drawing axes labels and ticks */
/* output text with lower left baseline at the current pen position. */
/* presumes that str will fit and in particular that coordinates
are in the viewport */
/* uses current text color and 12-point Times New Roman */
/* maximum length of s is 127 characters */
/* Does NOT change theDevice.x and theDevice.y, i.e., the current location
does NOT change */
/* Puts $ around s and passes it to svgSymbolText;
But, if it already begins
with $, don't add another pair. */
{ double u,v;
char buffer[130];
int byteswritten;
world_to_pixel(theDevice.x,theDevice.y,&u,&v); // u and v are viewport pixel coordinates
u += theDevice.pxmin;
v += theDevice.pymin;
if(s[0] != '$')
{ buffer[0] = '$';
strcpy(buffer+1,s);
strcat(buffer,"$");
s = buffer;
}
int width = (int) svg_text_width(s,0);
int ascent = 13; // for 10-point type; seems ok with 12 too
if(*next != '\0')
assert(0);
byteswritten = svgSymbolText(s,next, buffersize, theDevice.textcolor,
u,v-ascent,12,&width); // 12 means 12-point font
assert(strlen(next) == byteswritten);
buffersize -= byteswritten;
next += byteswritten;
}
/*____________________________________________________________________*/
void filled_rect(double x1, double y1, double x2, double y2)
/* draw a filled rectangle with opposite corners at (x1,y1),(x2,y2),
using the current background color for the interior and the
current foreground color for the border. x1, y1, x2, y2
are in world coordinates.
*/
{ double a,b,c,d;
world_to_pixel(x1,y1,&a,&b); // a,b are pixel viewport coordinates
world_to_pixel(x2,y2,&c,&d);
a += theDevice.pxmin;
c += theDevice.pxmin;
b += theDevice.pymin;
d += theDevice.pymin;
svg_filledrectwithborder(a,b,c,d,get_graphbackgroundcolor(),get_graphpencolor());
}
/*___________________________________________________________________*/
void svg_rect(double x1, double y1, double x2, double y2, unsigned color)
/* draw a rectangle without fill in (device, not viewport) pixel coordinates,
using the specified color for the outline, with stroke-width 1.
*/
{
char colorbuf[32];
svg_colorstring(colorbuf,color);
double x = MIN(x1,x2);
double y = MIN(y1,y2);
double width = fabs(x2-x1);
double height = fabs(y2-y1);
sprintf(next,
"<rect x=\"%.2lf\" y=\"%.2lf\" width=\"%.2lf\" height=\"%.2lf\" style=\"fill:none; stroke:%s; stroke-width:1;\"/>\n", x,y,width,height,colorbuf);
int byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
assert(buffersize > 100);
}
/*___________________________________________________________________*/
void svg_style_rectangles(int rgb)
/* output something like
<style>
.svgContainer rect {
stroke: none;
fill: rgb
}
</style>*/
{ char colorbuf[32];
svg_colorstring(colorbuf,rgb);
sprintf(next,
"\n<style>\n .graph rect{stroke:%s;fill:%s;}\n</style>\n",colorbuf,colorbuf);
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
}
/*___________________________________________________________________*/
int svg_unstyledrect(double x1, double y1, double x2, double y2)
/* draw a rectangle in pixel coordinates, without specifying sroke or fill,
to save space in relation graphs. To use this you must precede it by
calling style_rectangles.
Return 0 for success, 1 for out of space in outbuffer.
*/
{
double x = MIN(x1,x2);
double y = MIN(y1,y2);
double width = fabs(x2-x1);
double height = fabs(y2-y1);
if (buffersize <= 1000)
{ // printf("%.2000s",theDevice.outbuffer + strlen(theDevice.outbuffer)-2000);
return 1; // out of space.
}
sprintf(next,
"\n<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"/>\n", (int)(x+0.5), (int)(y+0.5),(int)(width+0.5),(int)(height+0.5));
/* The following sends an abbreviated form, that can be decoded
by decode2() in GraphDoc.php; so if you use this, uncomment decode2.
However, decode2 uses PHP regular expressions and is slow and runs out of memory,
hence I didn't use this method after all.
*/
/* sprintf(next,
"\n<Q %d %d %d %d/>",(int)(x+0.5), (int)(y+0.5),(int)(width+0.5),(int)(height+0.5)
); */
int byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
assert(*next == '\0'); // left by sprintf
return 0;
}
/*___________________________________________________________________*/
void svg_filledrect(double x1, double y1, double x2, double y2, unsigned color)
/* draw a rectangle without stroke in pixel coordinates,
using the specified color for the fill.
*/
{
char colorbuf[32];
svg_colorstring(colorbuf,color);
double x = MIN(x1,x2);
double y = MIN(y1,y2);
double width = fabs(x2-x1);
double height = fabs(y2-y1);
sprintf(next,
"<rect x=\"%.2lf\" y=\"%.2lf\" width=\"%.2lf\" height=\"%.2lf\"style=\"stroke:none;fill:%s;\"/>\n", x,y,width,height,colorbuf);
int byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
assert(*next == '\0'); // left by sprintf
if (buffersize <= 100)
{ printf("%.2000s",theDevice.outbuffer + strlen(theDevice.outbuffer)-2000);
assert(0);
}
}
/*___________________________________________________________________*/
void svg_filledrectwithborderandclass(char *class, double x1, double y1, double x2, double y2, unsigned fillcolor, unsigned bordercolor)
/* draw a rectangle without stroke in pixel coordinates,
using the specified color for the fill, and giving the element the specified class.
*/
{
char fillcolorbuf[32];
svg_colorstring(fillcolorbuf,fillcolor);
char bordercolorbuf[32];
svg_colorstring(bordercolorbuf,bordercolor);
double x = MIN(x1,x2);
double y = MIN(y1,y2);
double width = fabs(x2-x1);
double height = fabs(y2-y1);
sprintf(next,
"<rect class=\"%s\" x=\"%.2lf\" y=\"%.2lf\" width=\"%.2lf\" height=\"%.2lf\" style=\"stroke:%s; fill:%s;\"/>\n", class,x,y,width,height,bordercolorbuf,fillcolorbuf);
int byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
assert(buffersize > 100);
}
/*___________________________________________________________________*/
void svg_filledrectwithborder( double x1, double y1, double x2, double y2, unsigned fillcolor, unsigned bordercolor)
/* draw a rectangle without stroke in pixel coordinates,
using the specified color for the fill. (But don't give it a class.)
*/
{
char fillcolorbuf[32];
svg_colorstring(fillcolorbuf,fillcolor);
char bordercolorbuf[32];
svg_colorstring(bordercolorbuf,bordercolor);
double x = MIN(x1,x2);
double y = MIN(y1,y2);
double width = fabs(x2-x1);
double height = fabs(y2-y1);
sprintf(next,
"<rect x=\"%.2lf\" y=\"%.2lf\" width=\"%.2lf\" height=\"%.2lf\" style=\"stroke:%s; fill:%s;\"/>\n", x,y,width,height,bordercolorbuf,fillcolorbuf);
int byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
assert(buffersize > 100);
}
/*____________________________________________________________________*/
void polygon(double *x, double *y, int npoints)
/* fill the polygon defined by the given arrays of world coordinates.
The polygon is filled using the current graphbackgroundcolor and outlined
using the current pen.
*/
{ POINT *p;
int i;
p = (POINT *) calloc(npoints, sizeof(POINT));
if(p == NULL)
{ printf("Too many points (%d) in polygon\n",npoints);
return; // without drawing the desired polygon. But don't crash.
}
/* In Windows 3.1, a POINT was two ints, and ints are 2 bytes.
In Win32, a POINT was two longs, and both longs and ints were 4 bytes.
Now a POINT is two doubles.
*/
for(i=0;i<npoints;i++)
world_to_pixel2(x[i],y[i], &p[i].x, &p[i].y);
svg_polygon(p,npoints,theDevice.pencolor,theDevice.graphbackgroundcolor,theDevice.linewidth);
free(p);
}
/*____________________________________________________________________*/
void fill_polygon(double *x, double *y, int npoints)
/* fill the polygon defined by the given arrays of coordinates,
using the current graphbackgroundcolor. The outline of the polygon is NOT drawn.
*/
{ POINT *p;
int i;
p = (POINT *) calloc(npoints, sizeof(POINT));
if(p == NULL)
{ printf("Too many points (%d) in polygon\n",npoints);
return; // without drawing the desired polygon. But don't crash.
}
for(i=0;i<npoints;i++)
world_to_pixel2(x[i],y[i], &p[i].x, &p[i].y);
svg_filledPolygon(p,npoints,theDevice.graphbackgroundcolor);
free(p);
}
/*___________________________________________________________________*/
void svg_polygon(POINT *p,int npoints,unsigned strokecolor,unsigned fillcolor,double linewidth)
/* p points to an array of npoints POINTS */
{
char fillcolorbuf[32];
svg_colorstring(fillcolorbuf,fillcolor);
char strokecolorbuf[32];
int byteswritten;
svg_colorstring(strokecolorbuf,strokecolor);
assert(*next == '\0');
sprintf(next,
"\n<polygon fill = \"%s\" stroke = \"%s\" stroke-width = \"%.2lf\" points=\"", fillcolorbuf, strokecolorbuf, linewidth);
byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
for(int k = 0;k<npoints;k++)
{ sprintf(next,"%.2lf,%.2lf ", p[k].x, p[k].y);
byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
}
sprintf(next,"\"/>");
byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten; byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
}
/*___________________________________________________________________*/
void svg_filledPolygon(POINT *p,int npoints,unsigned fillcolor)
/* p points to an array of npoints POINTS */
{
char fillcolorbuf[32];
svg_colorstring(fillcolorbuf,fillcolor);
int byteswritten=0;
assert(*next == '\0');
sprintf(next,
"\n<polygon fill = \"%s\" stroke = \"none\" points=\"", fillcolorbuf);
byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
for(int k = 0;k<npoints;k++)
{ sprintf(next,"%.2lf,%.2lf ", p[k].x, p[k].y);
byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
}
sprintf(next,"\"/>");
byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten; byteswritten = (int)strlen(next);
next += byteswritten;
buffersize -= byteswritten;
}
/*______________________________________________________________*/
void defineArrowhead(unsigned rgb)
// call this function once before calling drawArrow(),
// so arrowhead will be defined. It will be colored with color rgb.
// It must be called where the output will go inside <svg> tags.
{ int byteswritten = 0;
char colorbuf[32];
svg_colorstring(colorbuf,rgb);
const char *arrowdefinition =
"<defs>\n"
"\t <marker id=\"arrowhead\" markerWidth=\"5\" markerHeight=\"3.5\"\n"
"\t refX=\"5\" refY=\"1.75\" orient=\"auto\">\n"
"\t <polygon points=\"0 0, 5 1.75, 0 3.5\" fill=\"%s\" />\n"
"\t </marker>\n"
"</defs>\n";
if(buffersize < strlen(arrowdefinition)+1000)
assert(0);
sprintf(next,arrowdefinition,colorbuf);
byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
}
/*__________________________________________________________*/
static void end_pathWithMarker(const char *marker)
/*
Otherwise marker is for example "arrowhead", which must b
the id of an element that is defined in the HTML context where
this code ends up after processing by PHP.
After writing the lines specifying marker and color and strokewidth,
close the path data attribute and the path element as in end_path.
*/
{
// Ensure there is no unsaved data in 'next'
if (*next != '\0') {
printf("%s\n", next);
assert(0);
}
// Ensure we are currently in a path
if (inpath == 0) {
assert(0);
}
// Marker specification string
char markerspec[512];
snprintf(markerspec, sizeof(markerspec),
"marker-end=\"url(#%s)\"\n ",
marker);
// space after \n is vital!
// Ensure buffer is large enough
if (buffersize < strlen(markerspec) + 5) {
assert(0);
}
// Output the closing quote for the data
sprintf( next, "\"\n");
// Copy the markerspec to next buffer
strcat(next, markerspec);
// close the path element
strcat(next, " />\n");
// Adjust pointers and sizes
int byteswritten = (int) strlen(next);
next += byteswritten;
buffersize -= byteswritten;
inpath = 0;
// Ensure buffer is now empty
if (*next != '\0') {
printf("%s\n", next);
assert(0);
}
}/*__________________________________________________________*/
void arrow_to(double x, double y)
// to be called inside a path only, and assumes defineArrowhead has been called.
{ assert(inpath);
line_to(x, y);
end_pathWithMarker("arrowhead"); // for now
begin_path();
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists