Sindbad~EG File Manager
/*
termtoSVG.c
MathXpert Calculus Assistant
Created by Michael Beeson on 10/30/23.
This is the SVG adaptation of display.c.
To understand the basics of the display of terms, see comments in display.c
12.17.23 put in assert(0) if svgwrite runs ou of space to write
12.21.23 corrected to use UTF-8 for sum and product
12.22.23 line 490, ORSTRING changed to "or". (now in textwidth.c)
12.23.23 moved svg_text_width to textwidth.c and modified code at 'primestring'
12.28.23 modified svg_display_def_integral and svg_display_integral to get the 'd' right.
12.27.23 added code to use get_minusspace()
12.28.23 modified write_indexedsum
12.29.23 modified where FRACT is display to get 1/2 spaced right in exponent.
12.30.23 altered svg_display_bra for big parens
1.11.24 changed ascent in svg_display_function
1.12.24, 1.13.24 changed svg_writelim, etc.
1.28.24 modified termtoAbsoluteSVGElement
1.31.24 made svg_write and svg_putchar use rgb directly
4.21.24 corrected display_integral and display_def_integral, which used
b.size before b is initialized. (Compiler issued no warning!)
4.23.24 corrected display_def_integral and draw_integral and svg_vert_aux on EIGHTPOINT
4.24.24 termtoColoredSVG
5.14.24 modified display_indexed_sum and display_subscripted function to use rgb instead of attr
6.12.24 corrected display_eval
6.16.24 corrected svg_vert_aux2 to get big braces displayed in cases terms
6.16.24 modified text_style to put MAXFUNCTOR and MINFUNCTOR in Roman type
6.21.24 corrected svg_display_long_atom for subscripted variables.
8.8.24 added brackets in svg_write_dndxn to avoid a warning
deleted unused variable in svg_display_function
8.10.24 deleted unused variable around line 1328
8.15.24 added code for LIST in two places in svg_display
9.11.24 changed draw_vert to accept additional parameter width, and
supplied arguments where it's called.
11.21.24 changed 2 to 1.8 around line 551, threshhold for using large parens
12.5.24 wrote termtoSVGElementNoPadding and called it in sendParameters
12.6.24 modified blockedTermtoAbsoluteSVGElement to make titles initially invisible.
12.6.24 changed draw_sqrt to draw the hook a little lower.
3.14.25 made sure top and left are always specified in pixels, as Firefox requires
*/
#include <assert.h>
#include <string.h>
#include <stddef.h>
#include <math.h>
#include "terms.h"
#include "display.h"
#include "charstr.h"
#include "dispfunc.h"
#include "pstring.h" /* paren */
#include "display1.h"
#include "defns.h" /* needed by vaux.h */
#include "vaux.h" /* parameter, now needed in termtoSVG.h */
#include "termtoSVG.h"
#include "textwidth.h"
#include "rgb.h" /* only needed for outlines when debugging */
#define COLUMNWIDTHS(t,rows) (*(coord **) (ARGPTR(t) + (rows) + 1))
// temporary, for debugging
static char *next; // kept pointing to the byte after the last character written to out.
static char *last; // one past the last allocated space
static void svg_display(term expr, coord x, coord y, int rgb);
static int svg_textstyle = ROMAN;
/*_________________________________________________________*/
static int svgwrite(char *s, int x, int y, int sizein, int rgb)
/* write SVG graphics to static global next as a <text> element.
use color rgb. sizein should be EIGHTPOINT or TENPOINT.
Use static global svg_textstyle
x and y are in papyrus coordinates; we pass them to SVG
unchanged, so this presumes the SVG coordinates are also
papyrus coordinates.
SVG places the text with its baseline specified,
The code here computes the baseline as 'ascent' below the passed value of y,
where ascent is 13 for TENPOINT and 7 for EIGHTPOINT.
When this code is called to display an atom, y is passed as
size==TENPOINT? get_charheight()? : get_eightpointheight();
above the bottom of the block meant to to contain the atom, which
we can think of as the default glyph-containing rectangle.
with EIGHTPOINTHEIGHT equal to 7 and TENPOINTHEIGHT equal to 16,
y will be 16 or 7 and the baseline will be 16-13 = 3 or 7-7 = 0
Now letters both with and without descenders (a,x,j,p,q) all have
to (a) fit in that box (b) line up their baselines. Where
are the baseline and the water level?
Since + or -
signs are supposed to be at waterlevel, but their baseline as characters is
lower than the horizontal bar of the sign, those signs are written with
their baselines at y + waterlevel - signoffset. So signoffset should
be the distance from the horizontal part of the + or - sign to its baseline
as a character (or rather, as a glyph...so this might be font-dependent!)
*/
{
char colorbuf[32];
svg_colorstring(colorbuf, rgb);
int ascent,baseline;
int top = y;
int avail = (int) (last-next);
if(avail > 100)
{
char *style;
switch(svg_textstyle)
{ case ROMAN: style = "roman"; break;
case ITALIC: style = "italic"; break;
case SYMBOLFONT: style = "roman"; break;
default: assert(0);
}
if(sizein == EIGHTPOINT)
{
ascent = 5;
baseline = top + ascent;
sprintf(next,"<text x = \"%d\" y=\"%d\" style=\"font-style:%s;font-size:8pt;fill:%s\">%s </text>\n",x,baseline,style,colorbuf,s);
}
else
{
ascent = 13;
baseline = top + ascent;
sprintf(next,"<text x = \"%d\" y=\"%d\" style=\"font-style:%s;font-size:12pt;fill:%s;\">%s </text>\n",x,baseline,style,colorbuf,s);
}
next += strlen(next);
assert (next < last);
//printf("next = %lu, available: %d\n", (unsigned long) next, (int) (last-next)); // used for debugging
return 0;
}
else
{
printf("Out of space to write SVG\n;");
assert(0);
}
}
/*_________________________________________________________*/
static int svgputch(char c, int x, int y, int sizein, int color)
/* write SVG graphics to static *next in the specified color
*/
{ int avail = (int) (last-next);
if(avail > 2)
{
char s[2];
s[0] = c;
s[1] = 0;
return svgwrite(s,x,y,sizein,color);
}
else
return 1;
}
/*_________________________________________________________*/
int svgwriteText(char *s, char *SVGout, int color, int style)
/* Write an SVG text element containing s to SVGout
in the specified color. Writes the text at (0,8).
*/
{ char *temp = next;
next = SVGout;
int oldstyle = svg_textstyle;
svg_textstyle = style;
svgwrite(s,0,8,TENPOINT,color);
svg_textstyle = oldstyle;
next = temp;
return 0;
}
/*_____________________________________________________________________*/
static void svg_display_sqrt(term,coord,coord,int);
static void svg_display_root(term,coord,coord,int);
static void svg_display_subscripted_function(term,coord,coord,int);
static void svg_display_logb1(term,coord,coord,int);
static void svg_display_matrix(int,term,coord,coord,int);
static void svg_display_eval(term,coord,coord,int);
static void svg_display_limit(term,coord,coord,int);
static void svg_display_indexed_sum(term,coord,coord,int);
static void svg_display_prime(term,coord,coord,int);
static void svg_display_diff(term,coord,coord,int);
static void svg_display_hi_diff(term,coord,coord,int);
static void svg_display_integral(term,coord,coord,int);
static void svg_display_def_integral(term,coord,coord,int);
static void svg_display_function(term,coord,coord,int);
static void svg_display_binder(term,coord,coord,int);
static void svg_display_long_atom(term,coord,coord,int);
static void svg_write_args(term,coord,coord,int);
static void svg_draw_horz(int,coord,coord,int);
static void svg_write_ddx(term t, coord x, coord z, US sizein, int attr);
static void svg_draw_integral(unsigned short h,coord x,coord y,US sizein, int attr);
static void svg_write_sigma(char *sumsign,term index, term lo, term hi, coord x, coord w, US sizein, int attr);
static coord svg_write_lim(term subscript, int arity, term dir, coord x, coord z, US sizein, int attr);
static void svg_rectangle(coord x, coord y, coord width, coord height, char *color);
static void svg_draw_sqrt(coord width, coord height,coord x,coord y,int attr);
static void svg_display_bra( unsigned short b, /* b = PARENS, BRACES, BARS or BRACKETS */
unsigned short h, /* desired height, in coord units */
coord x, /* x-coordinate in window */
coord y, /* y-coordinate in window */
int attr, /* colors to use; nonzero for highlighted terms */
unsigned short dir, /* 0 for open paren, 1 for close paren */
unsigned short fontsize
);
static void svg_vert_aux(unsigned short h, coord x, coord y, int attr, char* top, char *middle, char *bottom, double midshift, US sizein);
/*___________________________________________________________*/
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
static void svg_rectangle(coord x, coord y,coord width, coord height, char *color)
{
sprintf(next,
"<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" style=\"stroke-width:0.7;stroke:%s;fill-opacity:0\" />\n",x, y, width, height,color);
next += strlen(next);
assert (next < last);
}
#pragma GCC diagnostic pop
/*___________________________________________________________*/
static void svg_draw_sqrt(coord width, coord height,coord x,coord y,int rgb)
/* draw a sqrt sign in the box whose upper left corner
is at (x,y), of the specified width and height.
That is, print the SVG commmands for drawing the sqrt sign to the
static variable *out.(
*/
{
char colorbuf[32];
svg_colorstring(colorbuf, rgb);
sprintf(next,"<polyline\n");
next += strlen(next);
double X0,Y0,X1,Y1,X2,Y2,X3,Y3,X4,Y4;
X0 = x; Y0 = (y+height*0.75);
X1 = x+2; Y1 = Y0 -2;
X2 = (x+5); Y2 = (y+ height -1);
X3 = X1+get_sqrtspace(); Y3 = (y+1);
X4 = (x+width); Y4 = Y3;
sprintf(next,"points=\"%lf %lf, %lf %lf, %lf %lf, %lf %lf, %lf %lf\"\n",
X0,Y0,X1,Y1,X2,Y2,X3,Y3,X4,Y4);
next += strlen(next);
assert(next +100 < last );
sprintf(next,"style=\"stroke: %s; fill: none; stroke-width:1; stroke-linecap:round;\"/>\n",colorbuf);
next += strlen(next);
sprintf(next,"<line %lf %lf %lf %lf style=\"stroke:%s; stroke-width:2;\"", X1, Y1, X2, Y2, colorbuf);
assert(next + 100 < last);
}
/*___________________________________________________________*/
static void svg_draw_horz(int width,coord x, coord y, int attr)
/* draw a horizontal line starting at (x,y) of specified width in character coordinates.
Use color get_textcolor if attr is zero, or if rgb is not zero, use attr as
an rbg value.
*/
{ int rgb;
if(attr)
// rgb = get_highlightcolor();
rgb = attr;
else
rgb = get_textcolor();
char colorbuf[32];
svg_colorstring(colorbuf, rgb);
sprintf(next,"<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" style=\"stroke:%s;\"/>\n",x,y,x+width,y,colorbuf);
next += strlen(next);
assert (next < last);
}
/*___________________________________________________________*/
static void svg_draw_vert(int height,double x, double y, double width, int attr)
/* draw a vertical line starting at (x,y) of specified height in character coordinates.
Use color get_textcolor if attr is zero, or if attr is not zero, use get_highlightcolor().
Use width for the SVG stroke-width.
*/
{ int rgb;
if(attr)
rgb = get_highlightcolor();
else
rgb = get_textcolor();
char colorbuf[32];
char temp[300];
svg_colorstring(colorbuf, rgb);
sprintf(temp,"<line x1=\"%lf\" y1=\"%lf\" x2=\"%lf\" y2=\"%lf\" style=\"stroke:%s;stroke-width:%lf\"/>\n",x,y,x,y+height,colorbuf,width);
int spaceavail = (int) (last-next);
unsigned long m = strlen(temp);
assert(m < 299);
// printf("remaining: %d\n",spaceavail); // debugging
if( m >= spaceavail)
assert(0);
strcat(next,temp);
next += strlen(next);
assert (next < last);
}
/*___________________________________________________________*/
int termtoSVG(term t, char *SVGout, int n, int x, int y)
/* Spec: SVGout points to an array of characters of length at least n
Fill that array with SVG code to display t starting at (x,y).
That is, the display will fill a rectangle with upper left
corner at (x,y). Add a null terminator at the end.
It is assumed that SVGOut has enough space; at present
there is no error return or exception, only an assertion failure.
The plan is to experiment how much space is needed and be sure to
allocate enough. This function does not put in <svg> or </svg>.
*/
{ term bt;
next = SVGout;
// printf("Assigning next %lu Available %d\n", (unsigned long)next,n); // debug
last = SVGout + n;
bblock(t,&bt);
svg_display(bt,x,y,0); // 0 tells it to look at COLOR(t) and use highlight or text color
return 0;
}
/*___________________________________________________________*/
int termtoColoredSVG(term t, char *SVGout, int n, int x, int y, int color)
/* like termtoSVG, but the color arg can be used to produce a colored term.
Passing 0 for color will cause the COLOR bit of t to be checked, and the
current text color or highlight color used accordingly. Passing nonzero,
color should be an RGB value. Black is the default text color, so you may
have trouble getting a black term if the text color is not black. So use
RGB(0,0,1) in that case.
*/
{ term bt;
next = SVGout;
// printf("Assigning next %lu Available %d\n", (unsigned long)next,n); // debug
last = SVGout + n;
bblock(t,&bt);
svg_display(bt,x,y,color); // 0 tells it to look at COLOR(t) and use highlight or text color
return 0;
}
/*___________________________________________________________*/
int stringtoSVGElement(char *x, char *id, char *SVGout, int n, long width, int color)
/* make an svg element containing a text element containing x and
write it to SVGout. The text element will have height 16 and the
specified width. n is the size available at SVGout.
return 0 for success. At present failures result in assertion failures.
*/
{ sprintf(SVGout, "<svg id=\"%s\" width=\"%ld\" height=\"%d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, width,24);
size_t byteswritten = strlen(SVGout);
assert(byteswritten + strlen(x) + 100 < n);
svgwriteText(x,SVGout + byteswritten,color,ROMAN);
strcat(SVGout,"</svg>\n");
return 0;
}
/*___________________________________________________________*/
int stringtoAbsoluteSVGElement(char *x, char *id, char *SVGout, int n, long width, int color,coord u, coord v)
/* make an svg element containing a text element containing x and
write it to SVGout. The text element will have height 16 and the
specified width. n is the size available at SVGout. The element will
have absolute positioning at (u,v), and it will have a viewBox 0 0 width height
return 0 for success. At present failures result in assertion failures.
*/
{ sprintf(SVGout, "<svg id=\"%s\" width=\"%ld\" height=\"%d\" viewBox= \"%d %d %ld %d\"; style=\"position:absolute;left: %dpx; top: %dpx;\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, width,24,0,0,width,24,u,v);
printf("<svg id=\"%s\" width=\"%ld\" height=\"%d\" viewBox= \"%d %d %ld %d\"; style=\"position:absolute;left: %dpx; top: %dpx;\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, width,24,0,0,width,24,u,v);
size_t byteswritten = strlen(SVGout);
assert(byteswritten + strlen(x) + 100 < n);
svgwriteText(x,SVGout + byteswritten,color,ROMAN);
strcat(SVGout,"</svg>\n");
return 0;
}
/*___________________________________________________________*/
int termtoSVGElement(term t, char *SVGout, char *id, char *class, int n)
/* similar to termtoSVG, but it writes an opening <svg id = ...
with a canvas just 16 units bigger than the term (8 above, 8 below, 8 left, 8 right).
n is the number of bytes available at SVGout.
*/
{ term bt;
next = SVGout;
last = SVGout+n;
bblock(t,&bt);
unsigned long width = WIDTH(bt)+16;
unsigned long height = HEIGHT(bt)+16;
sprintf(SVGout, "<svg class=\"%s\" id=\"%s\" width=\"%lu\" height=\"%lu\" viewBox = \" 0 0 %lu %lu\" xmlns=\"http://www.w3.org/2000/svg\">\n", class, id, width,height,width,height);
unsigned long byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 1000);
svg_display(bt,8,8,COLOR(t));
byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 100);
sprintf(next,"</svg>\n");
return 0;
}
/*___________________________________________________________*/
int termtoSVGElementNoPadding(term t, char *SVGout, char *id, char *class, int n)
/* similar to termtoSVGElement, but without the 8-pixel padding.
This is used for text intended for buttons; the padding makes the text off center.
*/
{ term bt;
next = SVGout;
last = SVGout+n;
bblock(t,&bt);
unsigned long width = WIDTH(bt);
unsigned long height = HEIGHT(bt);
sprintf(SVGout, "<svg class=\"%s\" id=\"%s\" width=\"%lu\" height=\"%lu\" viewBox = \" 0 0 %lu %lu\" xmlns=\"http://www.w3.org/2000/svg\">\n", class, id, width,height,width,height);
unsigned long byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 1000);
svg_display(bt,0,-2,COLOR(t));
byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 100);
sprintf(next,"</svg>\n");
return 0;
}
/*___________________________________________________________*/
int termtoAbsoluteSVGElement(term t, char *SVGout, char *id, char *class, int n,coord x, coord y, int color, coord* level, coord *width, coord *height)
/* similar to termtoSVGElement, but it gives the <svg> element the absolute
position property and locates its upper left corner at (x,y).
n is the size available at SVGout. Returns the water level and height of t in level and height
The term will be written in the specified color, if that is not zero, and if it
is zero, then the subterms will be written in textcolor or highlightcolor according
to their COLOR bit.
*/
{ term bt;
next = SVGout;
last = SVGout+n;
bblock(t,&bt);
*width = (coord)( WIDTH(bt) + 16);
*height = HEIGHT(bt)+16;
if ( *width < 1000)
{ // Note we leave space after the width entry so there is room to change it later
// note %03d in the next line, always use 3 digits for the width and pad with 0
if (class == NULL)
sprintf(SVGout, "<svg id=\"%s\" width=\"%03d\" height=\"%d\" style=\"position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, *width,*height,x,y,*width,*height);
else
sprintf(SVGout, "<svg id=\"%s\" class = \"%s\" width=\"%03d\" height=\"%d\" style=\"position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, class, *width,*height,x,y,*width,*height);
}
else
{
if (class == NULL)
sprintf(SVGout, "<svg id=\"%s\" width=\"%d\" height=\"%d\" style=\"position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, *width,*height,x,y,*width,*height);
else
sprintf(SVGout, "<svg id=\"%s\" class = \"%s\" width=\"%d\" height=\"%d\" style=\"position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, class, *width,*height,x,y,*width,*height);
}
unsigned long byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 1000);
svg_display(bt,8,8,color);
byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 100);
sprintf(next,"</svg>\n");
*level = LEVEL(bt);
return 0;
}
/*___________________________________________________________*/
int blockedTermtoAbsoluteSVGElement(term bt, char *SVGout, char *id, char *class, int n,coord x, coord y, int color, coord* level, coord *width, coord *height)
/* similar to termtoAbsoluteSVGElement, except it skips the bblock step, starting already
with a blocked input, and produces an invisible result. This is used for
titles only, and Javascript makes them visible once they're in the right place.
*/
{
next = SVGout;
last = SVGout+n;
*width = (coord)( WIDTH(bt) + 16);
*height = HEIGHT(bt)+16;
if ( *width < 1000)
{ // Note we leave space after the width entry so there is room to change it later
// note %03d in the next line, always use 3 digits for the width and pad with 0
if (class == NULL)
sprintf(SVGout, "<svg id=\"%s\" width=\"%03d\" height=\"%d\" style=\"position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" display = \"none\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, *width,*height,x,y,*width,*height);
else
sprintf(SVGout, "<svg id=\"%s\" class = \"%s\" width=\"%03d\" height=\"%d\" style=\"position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" display = \"none\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, class, *width,*height,x,y,*width,*height);
}
else
{
if (class == NULL)
sprintf(SVGout, "<svg id=\"%s\" width=\"%d\" height=\"%d\" style=\"position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, *width,*height,x,y,*width,*height);
else
sprintf(SVGout, "<svg id=\"%s\" class = \"%s\" width=\"%d\" height=\"%d\" style=\"position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, class, *width,*height,x,y,*width,*height);
}
unsigned long byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 1000);
svg_display(bt,8,8,color);
byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 100);
sprintf(next,"</svg>\n");
*level = LEVEL(bt);
return 0;
}
/*___________________________________________________________*/
int blockedTermtoAbsoluteSVGElement2(term bt, char *SVGout, char *id, char *class, int n,coord x, coord y, int color, coord* level, coord *width, coord *height, parameter *p)
/* similar to termtoAbsoluteSVGElement, except it skips the bblock step, starting already
with a blocked input, and produces an invisible result. This is used for
titles only, and Javascript makes them visible once they're in the right place.
if p is not NULL, it points to the active parameter, and we have to tag the
sent SVG element with "param=4.2222" where 4.2222 represents the value of that parameter.
*/
{
next = SVGout;
last = SVGout+n;
*width = (coord)( WIDTH(bt) + 16);
*height = HEIGHT(bt)+16;
if(p==NULL)
{ // should never happen, but if it somehow does,
return blockedTermtoAbsoluteSVGElement(bt,SVGout,id,class,n,x,y,color,level,width,height);
}
double pvalue = *(p->addr);
if (fabs(pvalue) < 1e-10) // Tiny threshold
pvalue = 0.0; // avoid printing "-0.0000"
if ( *width < 1000)
{ // Note we leave space after the width entry so there is room to change it later
// note %03d in the next line, always use 3 digits for the width and pad with 0
if (class == NULL)
sprintf(SVGout, "<svg id=\"%s\" width=\"%03d\" height=\"%d\" param=\"%.4f\" style=\"position:absolute;left: %dpx; top: %dpx; display:none;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, *width,*height,pvalue,x,y,*width,*height);
else
sprintf(SVGout, "<svg id=\"%s\" class = \"%s\" width=\"%03d\" height=\"%d\" param=\"%.4f\" style=\"position:absolute;left: %dpx; top: %dpx; display:none;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, class, *width,*height,pvalue,x,y,*width,*height);
}
else
{
if (class == NULL)
sprintf(SVGout, "<svg id=\"%s\" width=\"%d\" height=\"%d\" param=\"%.4f\" style=\"position:absolute;left: %dpx; top: %dpx;display:none;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, *width,*height,pvalue,x,y,*width,*height);
else
sprintf(SVGout, "<svg id=\"%s\" class = \"%s\" width=\"%d\" height=\"%d\" param=\"%.4f\" style=\"position:absolute;left: %dpx; top: %dpx;display:none;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, class, *width,*height,pvalue,x,y,*width,*height);
}
unsigned long byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 1000);
svg_display(bt,8,8,color);
byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 100);
sprintf(next,"</svg>\n");
*level = LEVEL(bt);
return 0;
}
/*___________________________________________________________*/
int termtoInvisibleSVGElement(term t, char *SVGout, char *id, char *class, int n,coord x, coord y, int color, coord* level, coord *width, coord *height)
/* similar to termtoAbsoluteSVGElement, but it gives the <svg> element the
style display:none. This is used for assumptions, hints, progress elements, etc.
At input, n is the number of bytes available at SVGout.
*/
{ term bt;
next = SVGout;
last = SVGout+n;
bblock(t,&bt);
*width = (coord)( WIDTH(bt) + 16);
*height = HEIGHT(bt)+16;
if ( *width < 1000)
{ // Note we leave space after the width entry so there is room to change it later
// note %03d in the next line, always use 3 digits for the width and pad with 0
if (class == NULL)
sprintf(SVGout, "<svg id=\"%s\" width=\"%03d\" height=\"%d\" style=\"display:none;position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, *width,*height,x,y,*width,*height);
else
sprintf(SVGout, "<svg id=\"%s\" class = \"%s\" width=\"%03d\" height=\"%d\" style=\"display:none;position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, class, *width,*height,x,y,*width,*height);
}
else
{
if (class == NULL)
sprintf(SVGout, "<svg id=\"%s\" width=\"%d\" height=\"%d\" style=\"display:none;position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, *width,*height,x,y,*width,*height);
else
sprintf(SVGout, "<svg id=\"%s\" class = \"%s\" width=\"%d\" height=\"%d\" style=\"display:none;position:absolute;left: %dpx; top: %dpx;\" viewBox = \" 0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\">\n", id, class, *width,*height,x,y,*width,*height);
}
unsigned long byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 1000);
svg_display(bt,8,8,color); // writes to static global next
byteswritten = strlen(next);
next += byteswritten;
n -= byteswritten;
assert(n > 100);
sprintf(next,"</svg>\n");
*level = LEVEL(bt);
return 0;
}
/*_____________________________________________________________*/
static void svg_display(term expr, coord x, coord y, int attr)
/* expr is a bblocked term. Generate SVG code to
display it with bounding-box upper-left at (x,y).
The SVG code is written to static global next if it will
fit before last; otherwise it terminates before last.
The size of the font will be that specified by b.size, where
b is the block of the term expr.
If attr is not zero, use it for the color of the term. If it
is zero, then color each subterm according to bit 15 of its
info field, using get_textcolor() if that bit is zero and
get_highlightcolor() if it is not zero.
*/
{ short height,level,newx,charheight;
long width;
block b;
term u;
unsigned short f = FUNCTOR(expr);
unsigned short g;
unsigned short arity = (unsigned short)(ARITY(expr)-1); /* arity of original expression */
/* the bblocked expression has one extra arg for the block */
if(f==MATRIX || f == LIST)
--arity; // bblocked MATRIX has two more args than unblocked
int i;
long ii;
int rgb;
if(attr)
rgb = attr;
else if(COLOR(expr))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
char colorbuf[32];
svg_colorstring(colorbuf, rgb);
if(f==ILLEGAL)
return; /* void term, no display, but this isn't an error */
b = GETBLOCK(expr);
width = b.w;
height = b.h;
level = b.wl;
// #define SHOWBOXES
#ifdef SHOWBOXES
svg_rectangle(x,y,(int)b.w,b.h,"red"); // for debugging
svg_draw_horz((int)b.w,(int)x, y+(int)b.wl,RGB(0,127,127)); // to show water levels
#endif
if(b.leftbra || b.rightbra)
/* some parentheses, brackets, braces, or bars are needed */
/* bblock produces only terms with rightbra and leftbra = 0 or 1,
but in breaking lines, split produces bblocked terms with
multiple unbalanced parentheses, so we have to display
terms with rightbra and leftbra > 1 */
{ block oldb;
coord savespace, topparen;
oldb = b;
savespace = save_space(expr);
if( b.bra != BARS && b.bra != BRACKETS && height < 1.5 * get_charspace())
{ /* use symbol-fontsize parens on polynomials */
svg_textstyle = SYMBOLFONT;
if(b.bra==PARENS)
{ if(b.leftbra)
{ if(b.size == TENPOINT)
{ topparen = (coord)(y + level - get_charspace()/2-1);
}
else
{ topparen = (coord)(y+level - get_eightpointheight()/2);
}
svgputch('(',x,topparen,b.size,rgb);
}
if(b.rightbra)
{ if(b.size == TENPOINT)
{ topparen = (coord)(y + level - get_charspace()/2-1);
}
else
{ topparen = (coord)(y+level - get_eightpointheight()/2);
}
svgputch(')',(coord)(x+width-get_parenwidth(b.size)),topparen,b.size, rgb);
}
}
if(b.bra == BARS)
{ if(b.leftbra)
{ svgwrite("┃",x,(coord)(y + level -get_charspace()/2),b.size,rgb);
if(savespace > get_charspace())
savespace = get_charspace();
/* print |x|, not | x | */
}
if(b.rightbra)
svgwrite("┃",(coord)(x+width-1),(coord)(y+level-get_charspace()/2),b.size,rgb);
}
}
else
{ int correction = b.size == EIGHTPOINT ? 8 : 0;
// without this the parens are way to high in EIGHTPOINT,
// but they are fine in TENPOINT, so the correction has
// to depend on the type size.
if(b.leftbra)
svg_display_bra((unsigned short) b.bra,height,x,y+correction,rgb,0,b.size);
/* display open bracket and space after */
/* and now the closing bracket if any */
if(b.rightbra)
{ coord backup;
if(FUNCTOR(expr) == ABSFUNCTOR || FUNCTOR(expr) == DET)
backup = 1;
else
backup = get_parenwidth(b.size);
svg_display_bra((unsigned short) b.bra,height,(coord)(x+width-backup),y+correction,rgb,1,b.size);
}
}
if(b.leftbra)
{ x = (coord)(x + savespace);
b.w = (coord)(b.w - savespace); /* adjust width for left parens */
}
if(b.rightbra)
b.w -= savespace;
b.leftbra=b.rightbra=0;
if(f == MATRIX || f == LINEARSYSTEM)
/* brackets stick out get_matrixspace()/2 above and below */
{ y = (coord)(y + get_matrixspace()/2);
b.h = (coord)(b.h - get_matrixspace());
}
SETBLOCK(expr,b);
svg_display(expr,x,y,attr);
SETBLOCK(expr,oldb);
return;
}
/* from here on, we are writing a term without brackets */
if (ISATOM(expr) || f==0) /* atom or object */
/* note: ISATOM works because bblock_atom artificially sets the
arity to zero */
{ g = VARNAME(f);
/* Putting a value > 256 into isalpha causes a GPF
because isalpha uses its arg as an offset into
an array of dimension 256. We want italic type
for subscripted variables x1, but not for theta_1.
*/
if(f==0 || /* object */
f == '=' || /* atoms with functor '=' are used in
displaying systems of linear equations */
f == '?' /* needed for some displayed menu strings */
)
svg_textstyle = ROMAN;
else if(isalpha(g) ||
f == FALSEFUNCTOR ||
f == TRUEFUNCTOR ||
f == UNDEFINED ||
f == BOUNDED_OSCILLATIONS || /* These print as 'undefined' */
f == UNBOUNDED_OSCILLATIONS
)
svg_textstyle = ITALIC;
else if(PREDEFINED_ATOM(g)) /* Greek letters, infinity, etc. */
svg_textstyle = ITALIC; /* Greek letters etc. are printed in italic */
else if(f == PSEUDOATOM)
{ char *z = * (( char **)(ARGPTR(expr)+1));
if(isalpha((unsigned char) z[0]) || z[0] == 32)
svg_textstyle = ROMAN; /* ' if ' for example, with spaces before and after */
else
svg_textstyle = SYMBOLFONT;
}
else /* user-defined atom */
{ svg_display_long_atom(expr,x,y,rgb);
return;
}
if(SUBSCRIPT(f))
{ svg_display_long_atom(expr,x,y,rgb);
return;
}
charheight = b.size == TENPOINT ? get_charspace() : get_eightpointheight();
height = HEIGHT(expr);
/* The fontsize character box is taller than height probably,
and we have to pass its upper coordinate to svgwrite */
y = (coord)(y - charheight + height);
char *myname = *( (char **)(ARGPTR(expr)+1));
svgwrite(myname,x,y,b.size,rgb);
return;
}
else
{
if(COLOR(expr))
{ for(int i= 1; i<= arity;i++)
HIGHLIGHT(ARG(i,expr));
}
}
if(attr == 0 && COLOR(expr) == 0)
rgb = 0; // in case some subterms of this highlighted term are highlighted,
// pass 0 for the attr when coloring the args.
// This only makes a difference if textcolor is not black.
switch(f)
{ case '-' :
svg_textstyle = SYMBOLFONT;
svgputch('-' ,x,(coord)(y+level-get_signoffset(b.size,'-')),b.size,rgb);
if(COLOR(expr))
HIGHLIGHT(ARG(1,expr));
if(BB_OBJECT(ARG(1,expr)))
svg_display(ARG(1,expr),(coord)(x+get_parenspace(b.size)/2 + get_minuswidth(b.size)),y,rgb);
else
svg_display(ARG(1,expr),(coord)(x+get_minuswidth(b.size) + get_minusspace(b.size)),y,rgb);
return;
case NOT :
svg_textstyle = SYMBOLFONT;
svgwrite("¬",x,(coord)(y+level-8),b.size,attr);
svg_display(ARG(1,expr),(coord)(x+get_charspace()+get_minusspace(b.size)),y,rgb);
return;
case '+' :
newx = x;
int color;
for(i=1; i<= arity; i++)
{ if(attr == 0)
color = (COLOR(ARG(i,expr)) ? get_highlightcolor(): get_textcolor());
else
color = attr;
if(FUNCTOR(ARG(i,expr)) == '-')
{
svg_textstyle = SYMBOLFONT;
svgputch('-',
(newx+ (i>1 ? get_minusspace(b.size):0)),
(coord)(y + level - get_signoffset(b.size,'-')),
b.size, color);
SETCOLOR(ARG(1,ARG(i,expr)),color);
newx = (coord)(newx + get_minuswidth(b.size) + get_minusspace(b.size));
if(i>1)
newx += get_minusspace(b.size);
}
else if( i > 1)
{
if(attr == 0)
color = (COLOR(ARG(i-1,expr)) && COLOR(ARG(i,expr))) ? get_highlightcolor(): get_textcolor();
else
color = attr;
svg_textstyle = SYMBOLFONT;
svgputch('+',(coord)(newx+get_sumspace(b.size)),
(coord)(y + level- get_signoffset(b.size,'+')),
b.size, color);
newx = (coord)(newx + 2*get_sumspace(b.size) + get_pluswidth(b.size));
}
if(FUNCTOR(ARG(i,expr)) == '-')
{ svg_display(ARG(1,ARG(i,expr)),newx,(coord)(y + level - LEVEL(ARG(1,ARG(i,expr)))),attr);
/* ARG(i,expr) is -summand; but we have already written
the minus sign so we want to display only
the summand itself; since ARG(i,expr) is a bblocked
term, the summand is ARG(1,ARG(i,expr)), not
ARG(0,ARG(i,expr)) */
/* line up the water levels */
newx = (coord)(newx + WIDTH(ARG(1,ARG(i,expr))));
}
else
{ svg_display(ARG(i,expr),newx,(coord)(y + level - LEVEL(ARG(i,expr))),color);
/* line up the water levels */
newx = (coord)(newx + WIDTH(ARG(i,expr)));
}
}
return;
case AND:
newx = x;
for(i=1; i<= arity; i++)
{ if( i> 1 && FUNCTOR(ARG(1,ARG(i,expr))) != ILLEGAL)
/* ARG(1, because ARG(i,expr) is a bblocked term
so ARG(0, is the block */
{ svg_textstyle = SYMBOLFONT;
svgputch('&',newx,(coord)(y + level - get_infixspace()),b.size,attr);
newx = (coord)(newx + functor_width(AND,b.size) + get_infixspace());
}
svg_display(ARG(i,expr),newx,(coord)(y + level - LEVEL(ARG(i,expr))),attr);
/* line up the water levels */
newx = (coord)(newx + WIDTH(ARG(i,expr)));
}
return;
case OR:
newx = x;
for(i=1; i<= arity; i++)
{ if( i> 1)
{ svg_textstyle = ROMAN;
int rgb; // color to use in writing "or"
if(attr)
rgb = attr;
else if(COLOR(expr))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
svgwrite("or",newx,(coord)(y + level - 9),b.size, rgb);
newx = (coord)(newx + functor_width(OR,b.size) + get_infixspace());
}
svg_display(ARG(i,expr),newx,(coord)(y + level - LEVEL(ARG(i,expr))),attr);
/* line up the water levels */
newx = (coord)(newx + WIDTH(ARG(i,expr)) + get_infixspace());
}
return;
case '*' :
newx = x;
for(i=1; i<= arity; i++)
{ int dotflag = 0; /* do we need to write a dot for multiplication ? */
u = ARG(i,expr);
if (i>1 &&
( BB_OBJECT(u)
/* a bblocked object; arity = 3 means original object had
arity 1. So put the multiplication dot */
|| FUNCTOR(u) == DEG
|| (FUNCTOR(u) == FACTORIAL && BB_OBJECT(ARG(1,u)))
|| (FUNCTOR(u)== '^' && BB_OBJECT(ARG(1,u))) /* e.g. 2˘3^2 */
|| (FUNCTOR(u) == '/' &&
BB_OBJECT(ARG(1,u)) &&
BB_OBJECT(ARG(2,u)) /* e.g. 2^1/3 */
)
)
)
{ svg_textstyle = SYMBOLFONT;
dotflag = 1;
coord y3;
if( b.size == TENPOINT)
y3 = y+level -7; // where to write the dot
else
y3 = y + level - 2;
svgwrite( "·",newx + 2,y3,b.size, rgb);
newx = (coord)(newx + get_dotspace());
}
if(i > 1 && !dotflag) /* add space before displaying u */
newx = (coord)(newx + product_spacing(ARG(i-1,expr),u));
svg_display(u,newx,(coord)(y + level - LEVEL(ARG(i,expr))),rgb);
/* line up all the water levels */
newx = (coord)(newx + WIDTH(u));
}
return;
case '^' :
b = GETBLOCK(ARG(1,expr));
if(b.extra && f && arity && PREFIX(FUNCTOR(ARG(1,expr))))
/* infix exponent like sin^2 x */
{ unsigned short g = FUNCTOR(ARG(1,expr));
char fname[32];
functor_string(g,SCREEN,fname);
svg_textstyle = ROMAN;
svgwrite( fname,x,(coord)(y+LEVEL(ARG(2,expr))),b.size, attr);
x = (coord)(x + functor_width(g,(unsigned short) b.size) + get_powerspace());
svg_display(ARG(2,expr),x,y,rgb);
/* bottom of exponent 'raisedexponent' above (water level + get_charspace()/2) */
x= (coord)(x + WIDTH(ARG(2,expr)) + get_powerspace());
svg_display(ARG(1,ARG(1,expr)),x,(coord)(y+level-LEVEL(ARG(1,ARG(1,expr)))),rgb);
return;
}
/* Now in case it's an ordinary, not an infix, exponent */
{ svg_display(ARG(1,expr),x,(coord)(y + level - LEVEL(ARG(1,expr))), attr);
x = (coord)(x + WIDTH(ARG(1,expr)));
if(FUNCTOR(ARG(1,expr)) || ARITY(ARG(1,expr)) > 1)
/* not a void base */
x += get_powerspace();
svg_display(ARG(2,expr),x,y, rgb);
return;
}
case '/' : /* fraction, vertical display */
ii = (width - WIDTH(ARG(1,expr)))/2; /* how far to move to center numerator */
svg_display(ARG(1,expr),(coord)(x+ii),y,attr);
svg_draw_horz((unsigned short) width,x,(coord)(y+level),rgb);
ii = (width - WIDTH(ARG(2,expr)))/2;
y = (coord)(y + level + get_belowfractionspace());
if(GETBLOCK(ARG(2,expr)).leftbra)
y = (coord)(y + get_belowfractionspace());
/* more space is needed when the denominator is
bracketed, because when it's not bracketed,
we rely on 'internal leading' space, which
isn't there above the brackets. */
svg_display(ARG(2,expr),(coord)(x+ii),y,rgb);
return;
case FRACT : /* fraction, horizontal display */
svg_display(ARG(1,expr),x,(coord)(y+LEVEL(expr)-LEVEL(ARG(1,expr))),rgb);
x = (coord)(x + WIDTH(ARG(1,expr)));
svg_textstyle = SYMBOLFONT;
if(b.size == TENPOINT)
{ svgputch('/',x,(coord)(y+LEVEL(expr)-get_charspace()/2),b.size,rgb);
x = (coord)(x + 5);
}
else
{ svgputch('/',x,(coord)(y+LEVEL(expr)-get_eightpointheight()/2+1),b.size, rgb);
x = (coord)(x+5); /* no space before and after */
}
svg_display(ARG(2,expr),x,(coord)(y+LEVEL(expr)-LEVEL(ARG(2,expr))),rgb);
return;
case BINOMIAL :
svg_display(ARG(1,expr),(coord)(x+1+(width - WIDTH(ARG(1,expr)))/2),y,rgb);
svg_display(ARG(2,expr),(coord)(x+1+(width - WIDTH(ARG(2,expr)))/2),(coord)(y+level),rgb);
/* No get_abovefractionspace() and get_belowfractionspace() is used with BINOMIAL */
return;
/* CASES doesn't have to be here as it is bblocked with functor MATRIX */
case LINEARSYSTEM:
svg_display_matrix(1,expr,x,y,rgb);
break;
case LIST: // deliberate fall-through
case MATRIX:
svg_display_matrix(0,expr,x,y,rgb);
break;
case EVAL:
svg_display_eval(expr,x,y,rgb);
break;
case DET: /* fall through */
case ABSFUNCTOR:
svg_display(ARG(1,expr),x,y,rgb);
break;
case DIFF:
if(arity == 2)
svg_display_diff(expr,x,y,rgb);
else
svg_display_hi_diff(expr,x,y,rgb);
break;
case INTEGRAL:
if (arity == 2)
svg_display_integral(expr,x,y,rgb);
if (arity == 4)
svg_display_def_integral(expr,x,y,rgb);
break;
case PR:
svg_display_prime(expr,x,y,rgb);
break;
case LIMIT:
svg_display_limit(expr,x,y,rgb);
break;
case SQRT:
svg_display_sqrt(expr,x,y,rgb);
break;
case ROOT:
svg_display_root(expr,x,y,rgb);
break;
case BESSELJ: /* fall-through */
case BESSELY:
case BESSELI:
case BESSELK:
case POLYGAMMA:
case BERNOULLI:
case EULERNUMBER:
case CONSTANTOFINTEGRATION:
svg_display_subscripted_function(expr,x,y,rgb);
break;
case LOGB:
svg_display_logb1(expr,x,y,rgb);
break;
case SUM :
svg_display_indexed_sum(expr,x,y,rgb);
break;
case PRODUCT:
svg_display_indexed_sum(expr,x,y,rgb); /* "sum" not a typo */
break;
case FLOOR:
svg_textstyle = SYMBOLFONT;
for(i=0;i<height-12;i += 12)
svgwrite("┃",x-8,(coord)(y + i), b.size,attr);
svgwrite("┗",x-8,(coord)(y+height-8),b.size,attr); // U-9490
x = (coord)(x + get_floorwidth()); /* for | */
svg_display(ARG(1,expr),x,y,rgb);
x = (coord)(x + WIDTH(ARG(1,expr)))+get_floorwidth()-8;
// the -8 is because the vertical part of floor is exactly in the middle of the 16 by 16 glyph box, and we want it to print exactly on the bounding box boundary.
svg_textstyle = SYMBOLFONT;
for(i=0;i<height-12;i += 12)
svgwrite("┃",x,(coord)(y + i), b.size, rgb);
svgwrite("┛",x,(coord)(y+height-8),b.size, rgb); // U + 9499
return;
case FORALL:
case EXISTS:
case LAMBDA:
svg_display_binder(expr,x,y,rgb);
return;
default :
svg_display_function(expr,x,y,rgb);
return;
}
}
/*__________________________________________________________________________*/
static void svg_display_diff(term newexpr,coord x,coord y,int attr)
/* display du/dx if u is atomic, or d/dx u if u is not atomic */
/* x must always be atomic, but it CAN be subscripted, as can u. */
{ term u,t;
int rgb;
if(attr)
rgb = attr;
else if(COLOR(newexpr))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
coord width = (coord)WIDTH(newexpr);
coord level = LEVEL(newexpr);
coord width1,width2,level1,z;
coord charheight = (coord)(SIZE(newexpr) == TENPOINT ? get_charspace() : get_eightpointheight());
coord dwidth = atom_width('d',(short)SIZE(newexpr));
u = ARG(1,newexpr);
t = ARG(2,newexpr); /* doing du/dt */
width1 = (coord) WIDTH(u);
width2 = (coord) WIDTH(t);
level1 = LEVEL(u);
if(ISATOM(u) || (BB_OBJECT(u) && TYPE(u) != BIGNUM))
{ width1 = (coord)(width1 + dwidth); /* width of 'numerator' */
width2 = (coord)(width2 + dwidth); /* width of 'denominator' */
z = (coord)(x + (width-width1)/2);
svg_textstyle = ITALIC;
svgputch('d',z,y,SIZE(newexpr),rgb);
z = (coord)(z + dwidth);
if(HEIGHT(u) < charheight)
svg_display(u,z,(coord)(y + charheight-HEIGHT(u)),rgb);
else
svg_display(u,z,y,rgb);
y = (coord)(y+level);
svg_draw_horz(width, x, y, rgb);
z = (coord)(x + (width - width2)/2);
svg_textstyle = ITALIC;
y = (coord)(y + get_belowfractionspace());
svgputch('d', z, y,SIZE(newexpr),rgb);
z = (coord)(z + dwidth);
if(HEIGHT(t) < charheight)
y = (coord)(y + charheight - HEIGHT(t));
svg_display(t,z, y, rgb);
return;
}
/* Now the numerator is not atomic, so we display like (d/dx) sin x */
svg_write_ddx(t,x,(coord)(y+level),SIZE(newexpr),rgb);
svg_display(u,(coord)(x+width-width1),(coord)(y+level-level1),rgb);
}
/*____________________________________________________________*/
static void svg_write_ddx(term t, coord x, coord z, US sizein, int attr)
/* z is the papyrus y-coordinate where you want the horizontal bar
in d/dt. Write d/dt with a horizontal bar, left-aligned with
papryus coordinate x */
{ int rgb;
if(attr)
rgb = attr;
else if(COLOR(t))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
coord dwidth = atom_width('d',sizein);
coord width2 = (coord)(WIDTH(t) + dwidth); /* width of 'denominator' */
coord charheight = sizein == TENPOINT ? get_charspace() : get_eightpointheight();
svg_textstyle = ITALIC;
svgputch('d',(coord)(x + width2 /2) ,(coord)(z - get_abovefractionspace() - charheight),sizein,attr);
svg_draw_horz(width2,x,z,rgb);
svgputch('d',x,(coord)(z + get_belowfractionspace()),sizein,attr);
if(HEIGHT(t) < charheight)
svg_display(t, (coord)(x + dwidth),(coord)(z + get_belowfractionspace() + charheight-HEIGHT(t)),rgb);
else
svg_display(t,(coord)(x + dwidth),(coord)(z + get_belowfractionspace()), rgb);
}
/*____________________________________________________________*/
static void svg_write_dndxn(term t, term n, coord x, coord z, US sizein, int attr)
/* z is the papyrus y-coordinate where you want the horzontal bar
in d^n /dt^n . Write d^n /dt^n with a horizontal bar, left-aligned with
papryus coordinate x */
{ int rgb;
if(attr)
rgb = attr;
else
{ if(COLOR(t))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
}
coord dwidth = atom_width('d', sizein);
coord charheight = (coord)(sizein == TENPOINT ? get_charspace() : get_eightpointheight());
coord w,secondrow;
coord width2 = (coord) WIDTH(t);
w = (coord)(x + width2/2);
svg_textstyle = ITALIC;
svgputch('d',w,(coord)(z - get_abovefractionspace() - charheight),sizein,rgb);
w = (coord)(w + dwidth + get_powerspace());
svg_display(n,w,(coord)(z-HEIGHT(n)-get_abovefractionspace() - 6),rgb);
width2 = (coord)(width2 + WIDTH(n) + dwidth + get_powerspace()); /* width of 'denominator' */
svg_draw_horz(width2,x,z,rgb);
svg_textstyle = ITALIC;
secondrow = (coord)(z + get_belowfractionspace() + HEIGHT(n) -(charheight-6));
svgputch('d',x,secondrow,sizein,rgb);
svg_display(t,(coord)(x + dwidth),(coord)(secondrow + (HEIGHT(t) < charheight ? charheight-HEIGHT(t) : 0)), rgb);
svg_display(n,(coord)(x + dwidth + WIDTH(t) + get_powerspace()), (coord)(z+get_belowfractionspace()), rgb);
}
/*__________________________________________________________________________*/
static void svg_display_hi_diff(term newexpr,coord x,coord y,int attr)
/* display higher-order derivatives like d^2/dx^2 */
{ term u,n,t;
int rgb;
if(attr)
rgb = attr;
else if(COLOR(newexpr))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
coord width = (coord) WIDTH(newexpr);
coord level = LEVEL(newexpr);
coord width1,width2,level1;
coord charheight = (coord)(SIZE(newexpr)== EIGHTPOINT ? get_charspace() : get_eightpointheight());
coord z;
coord dwidth = atom_width('d',(unsigned short)SIZE(newexpr));
u = ARG(1,newexpr);
n = ARG(3,newexpr);
t = ARG(2,newexpr); /* doing d^nu/dt^n */
width1 = (coord) WIDTH(u);
width2 = (coord) WIDTH(t);
level1 = LEVEL(u);
if( (ISATOM(u) || (BB_OBJECT(u) && TYPE(u) != BIGNUM)) &&
(ISATOM(n) || BB_OBJECT(n))
)
{ width1 = (coord)(width1 + dwidth + WIDTH(n) + get_powerspace()); /* width of 'numerator' */
width2 = (coord)(width2 + dwidth + WIDTH(n) + get_powerspace()); /* width of 'denominator' */
z = (coord)(x + (width-width1)/2);
svg_textstyle = ITALIC;
svgputch('d',z,(coord)(y+1),SIZE(newexpr),rgb);
z = (coord)(z + dwidth + get_powerspace());
svg_display(n,z,y,rgb);
z = (coord)(z + WIDTH(n));
if(HEIGHT(u) < charheight)
svg_display(u,z,(coord)(y + 3+charheight-HEIGHT(u)),rgb);
else
svg_display(u,z,y+3,rgb);
y = (coord)(y+level);
svg_draw_horz(width, x, y, rgb);
z = (coord)( x + (width - width2)/2);
svg_textstyle = ITALIC;
y = (coord)(y + get_belowfractionspace());
svgputch('d',x,y,SIZE(newexpr),rgb);
if(HEIGHT(t) < charheight)
svg_display(t, (coord)(x + dwidth),(coord)(y+3 + charheight-HEIGHT(t)),rgb);
else
svg_display(t,(coord)(x + dwidth),y+2, rgb);
svg_display(n,(coord)(x + dwidth + WIDTH(t)),y,rgb);
return;
}
/* Now the numerator is not atomic, so we display like d^n /dx^n sin x,
but of course using a horizontal fraction bar. It is not assumed
that n is atomic, either.
*/
svg_write_dndxn(t,n,x,(coord)(y+level),SIZE(newexpr),rgb);
svg_display(u,(coord)(x+width-width1),(coord)(y+level-level1),rgb);
}
/*____________________________________________________________________*/
static void svg_display_integral(term newexpr,coord x,coord y, int attr)
/* Display an indefinite integral. The integral sign is drawn as
high as the integrand, i.e. the integrand never sticks up higher
than the integral sign.
*/
{ term u,t;
block b = GETBLOCK(newexpr);;
coord h,level;
coord dwidth = atom_width('d',b.size);
h = b.h;
level = b.wl;
u=ARG(1,newexpr);
t=ARG(2,newexpr);
svg_draw_integral(h,x,y, SIZE(newexpr),attr);
x = (coord)(x + get_integralsignwidth() + get_productspace(b.size));
svg_display(u,x,(coord)(y+level-LEVEL(u)),attr);
x = (coord)(x + WIDTH(u) + get_productspace(b.size));
if(b.extra)
return; /* don't display 'dx' ; such terms are made by
'split' for the first line of a multi-line display */
svg_textstyle = ITALIC;
svgputch('d',x, (coord)(y + level - LEVEL(t)-2),SIZE(newexpr),attr);
// the 2 is an empircal adjusment to line up d with x.
x = (coord)(x + dwidth); /* for the 'd' you just wrote; but no space after 'd' */
svg_display(t,x,(coord)(y + level - LEVEL(t)),attr);
}
/*____________________________________________________________________*/
static void svg_display_def_integral(term newexpr,coord x,coord y, int attr)
/* Display a definite integral. The integral sign is drawn so that
the upper limit will be at the top of the term, i.e. the integrand
never sticks up higher than the upper limit.
*/
{ term u,t,lo,hi;
coord height,level;
coord h,s;
block b = GETBLOCK(newexpr);
coord dwidth = atom_width('d',b.size);
height = b.h;
level = b.wl;
u = ARG(1,newexpr);
t = ARG(2,newexpr);
lo = ARG(3,newexpr);
hi = ARG(4,newexpr);
h = (coord)(height - (HEIGHT(lo)+get_lowerlimy() + get_upperlimy() + HEIGHT(hi) - 2*EIGHTPOINTHEIGHT));
/* h is the height of the integral sign */
/* Now find the top of the integral sign */
s = (coord)(y + HEIGHT(hi) - EIGHTPOINTHEIGHT + get_upperlimy());
svg_draw_integral(h,x,s,b.size,attr);
s = (coord)(y + height - HEIGHT(lo) + get_lowerlimy());
x = (coord)(x + get_integralsignwidth()); /* for the integral sign */
svg_display(lo,(coord)(x+get_lowerlimx()),s,attr);
svg_display(hi,(coord)(x+get_upperlimx()),y,attr);
x = (coord)(x + MAXIMUM(WIDTH(lo)+get_lowerlimx(),WIDTH(hi)+get_upperlimx()) + get_productspace(b.size));
/* get_productspace(b.size) after the limits before the integrand */
s = (coord)(y + level - LEVEL(u));
svg_display(u,x,s,attr);
x = (coord)(x + WIDTH(u) + get_productspace(b.size));
if(b.extra)
return; /* don't display 'dx' ; such terms are made by
'split' for the first line of a multi-line display */
s = (coord)(y + level - LEVEL(t)); /* put the 'dx' at waterlevel */
svg_textstyle = ITALIC;
if(b.size == TENPOINT)
{ svgputch('d',x, (coord)(y + level - LEVEL(t)-2),b.size,attr);
// the 2 is an empirical adjustment to line up d with x.
}
else
{ svgputch('d',x, (coord)(y + level - LEVEL(t)+3),b.size,attr);
}
x = (coord)(x + dwidth); /* for the 'd' you just wrote; but no space after 'd' */
svg_display(t,x,(coord)(y + level - LEVEL(t)),attr);
}
/*____________________________________________________________________*/
/* newexpr has arity 5 */
/* This function is used for indexed products as well as sums */
static void svg_display_indexed_sum(term newexpr,coord x,coord y, int attr)
{ term u,lo,hi,index;
int rgb;
if(attr)
rgb = attr;
else
rgb = COLOR(newexpr)? get_highlightcolor(): get_textcolor();
coord width = (coord) WIDTH(newexpr);
coord level = LEVEL(newexpr);
int f = FUNCTOR(newexpr);
if(ARITY(newexpr) == 6)
{ /* display with... */
svg_display(ARG(5,newexpr),x,y,rgb);
return;
}
u= ARG(1,newexpr);
index = ARG(2,newexpr);
lo = ARG(3,newexpr);
hi = ARG(4,newexpr);
char sign[3];
strcpy(sign, f==SUM ? "\xCE\xA3" : "\xCE\xA0"); // capital Sigma or Pi
svg_write_sigma(sign,index,lo,hi,x,(coord)(y+level),SIZE(newexpr),rgb);
svg_display(u,(coord)(x+width-WIDTH(u)),(coord)(y+level-LEVEL(u)),rgb);
}
/*________________________________________________________________*/
static void svg_write_sigma(char *sumsign,term index, term lo, term hi, coord x, coord w,
US sizein, int rgb)
/* write the sum sign and the lower and upper indices of a bblocked
term whose water level is at papyrus y-coordinate w, and whose
left side is a papyrus x-coordinate x, with rgb as the color
*/
{ coord charheight = (coord)(sizein == TENPOINT ? get_charspace() : get_eightpointheight());
unsigned long subscriptwidth = WIDTH(lo) + WIDTH(index) + 12;
coord subscriptlevel;
coord z = x;
coord offset = (coord)(MAXIMUM(subscriptwidth, WIDTH(hi))/2);
/* this is where the center of the sum sign should be */
offset = (coord)(offset - (sizein == TENPOINTHEIGHT ? 6 : 5)); /* empirically adjusted numbers */
svg_textstyle = SYMBOLFONT;
/* Now write the summation sign or product sign as the case may be */
svgwrite(sumsign,(coord)(x + offset),(coord)(w-charheight/2),sizein,rgb);
subscriptlevel = MAXIMUM(LEVEL(lo),LEVEL(index));
if(WIDTH(hi) > subscriptwidth)
{ z = (coord)(z + (WIDTH(hi) - subscriptwidth + 3)/2);
/* +1 to cause uncentered display to shift right a half , not left */
svg_display(index,z,(coord)(w+ charheight/2 + subscriptlevel - LEVEL(index)),rgb);
/* w + charheight/2 is the bottom of the summation sign;
go LEVEL(lo)-LEVEL(index) below that */
svgputch('=',(coord)(z+WIDTH(index)+1),(coord)(w + charheight/2 - get_eightpointheight()/2 + subscriptlevel+2),EIGHTPOINT,rgb);
// +2 added 1.2.24;
svg_display(lo,(coord)(z+WIDTH(index)+ 21),(coord)(w + charheight/2 + subscriptlevel - LEVEL(lo)),rgb);
/* 13 is the width of = in EIGHTPOINT */
svg_display(hi,x,(coord)(w-HEIGHT(hi)-charheight/2),rgb);
}
else
{ z = (coord)(z + (subscriptwidth - WIDTH(hi)+3)/2);
svg_display(index,x, (coord)(w+charheight/2 + subscriptlevel-LEVEL(index)),rgb);
svgputch('=',(coord)(x+WIDTH(index)+1),(coord)(w + charheight/2 -get_eightpointheight()/2 + subscriptlevel),EIGHTPOINT,rgb);
svg_display(lo,(coord)(x+WIDTH(index)+12),(coord)(w+charheight/2 + subscriptlevel-LEVEL(lo)),rgb);
/* 13 is the width of = in EIGHTPOINT */
svg_display(hi,z,(coord)(w-HEIGHT(hi)-charheight/2),rgb);
}
}
/*_______________________________________________________________________*/
static void svg_display_function(term newexpr,coord x,coord y, int attr)
{ term u;
block b = GETBLOCK(newexpr);
coord level = b.wl;
int rgb; // color to use in writing the function name
if(attr)
rgb = attr;
else if(COLOR(newexpr))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
unsigned short f = FUNCTOR(newexpr);
int charheight = (coord)(b.size == TENPOINT ? get_charspace() : get_eightpointheight());
char fname[32];
if(SUBSCRIPTARGS(newexpr))
{ svg_display_subscripted_function(newexpr,x,y,rgb);
return;
}
functor_string(f,SCREEN,fname); /* in dispfunc.c */
u = ARG(1,newexpr);
if(PREFIX(f))
{ if(f=='-')
svg_textstyle = SYMBOLFONT; /* wider width */
else
svg_textstyle = ROMAN; /* prefix function names in Roman type */
/* If the following lines change, change the corresponding code where
infix exponents are displayed, and also in bdisplay.c in ldisplay,
both where function and where infix exponents are displayed. */
coord ascent = b.size == TENPOINT ? 9 : 2;
svgwrite(fname,x,(coord)(y+level-ascent),SIZE(newexpr),rgb); /* at water level*/
x = (coord)(x + functor_width(f,(unsigned short)b.size));
x = (coord)(x + get_prefixspace());
if(HEIGHT(u) >= charheight)
svg_display(u,x,y,rgb);
else /* ln x for example; the 'x' is shorter than charheight */
svg_display(u,x,(coord)(y + charheight-HEIGHT(u)),rgb);
return;
}
if(POSTFIX(f))
{ svg_display(u,x,(coord)(y + level - LEVEL(u)),rgb);
x = (coord)(x + WIDTH(u)); /* no space after the argument */
svg_textstyle = SYMBOLFONT;
switch(f)
{ case DEG:
svgwrite(fname,x,y,SIZE(newexpr),rgb);
break;
default :
svgwrite(fname,x,(coord)(y+level-charheight/2),SIZE(newexpr),rgb);
}
return;
}
if(INFIX(f))
{ term v;
coord gap = get_infixspace();
int namelength = functor_width(f,b.size);
if(b.size == EIGHTPOINT)
gap = 2;
v = ARG(2,newexpr);
if(COLOR(newexpr))
{ HIGHLIGHT(u);
HIGHLIGHT(v);
}
svg_display(u,x,(coord)(y+level-LEVEL(u)),rgb);
x = (coord)(x + WIDTH(u) + gap);
if(f==MOD || f == MULTIPLICITY )
svg_textstyle = ROMAN;
else
svg_textstyle = SYMBOLFONT; /* all other infix operators are symbolic */
/* The symbol is going to come out about charheight/2 below
the y-coordinate passed to svgwrite, which will be interpreted
as the top of the fontsize character box
*/
svgwrite(fname,x,(coord)(y+level-charheight/2 ),SIZE(newexpr),rgb);
svg_display(v,(coord)(x+namelength + gap),(coord)(y+level-LEVEL(v)),rgb);
return;
}
else
{ svg_textstyle = text_style(f);
svgwrite(fname,x+2,(coord)(y+level-charheight/2-1),SIZE(newexpr),attr);
if(DEFINED_FUNCTION(f))
x = (coord)(x + svg_text_width(fname,(unsigned short)b.size));
else
x = (coord)(x + functor_width(f,(unsigned short) b.size));
svg_write_args(newexpr,x,y,attr);
return;
}
}
/*_________________________________________________________*/
static void svg_write_args(term newexpr,coord x,coord y,int rgb)
/* writes (x,y,z) for example; uses big brackets when appropriate */
{ int i;
block b = GETBLOCK(newexpr);
coord height = b.h;
coord level = b.wl;
unsigned short arity = (unsigned short)(ARITY(newexpr)-1); /* -1 because it's a bblocked term */
if (height >= level + 12)
svg_display_bra(PARENS,height,x,y,rgb,0,b.size);
else
{ svg_textstyle = ROMAN;
svgputch('(',x,(coord)(y+level-get_charspace()/2),SIZE(newexpr),rgb);
}
x += get_parenwidth(b.size); /* for the open bracket */
if(height > get_charspace())
x += get_parenspace(b.size); /* space after bracket */
for(i=1;i<=arity;i++)
{ svg_display(ARG(i,newexpr),x,(coord)(y + level - LEVEL(ARG(i,newexpr))),rgb);
x = (coord)(x + WIDTH(ARG(i,newexpr)));
if(i<arity)
{ svg_textstyle = ROMAN;
svgputch(',',x,(coord)(y + level-get_charspace()/2),SIZE(newexpr),rgb);
/* No space before or after comma */
x = (coord)(x + get_parenwidth(b.size));
}
}
if(height > get_charspace())
x += get_parenspace(b.size);
if( height >= level+12)
svg_display_bra(PARENS,height,x,y,rgb,1,b.size); /* close bracket */
else
{ svg_textstyle = ROMAN;
svgputch(')',x,(coord)(y+level-get_charspace()/2),SIZE(newexpr),rgb);
}
}
/*_______________________________________________________________________*/
/* newexpr is a bblocked PR(u,n) */
static void svg_display_prime(term newexpr,coord x,coord y,int attr)
{ term u,exp;
char *primestring; // signed so as to compare to UTF-8 character bytes
unsigned short f;
int rgb; // color to use in writing the function name
if(attr)
rgb = attr;
else if(COLOR(newexpr))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
int flag; /* same cases as in bblock_prime */
char fname[32];
coord level = LEVEL(newexpr);
u = ARG(1,newexpr);
f = FUNCTOR(u);
exp = ARG(2,newexpr);
if(ATOMIC(u) ||
(COMPOUND(u) && paren(PR,FUNCTOR(u),LEFT)) /* e.g. for u= s+t, s*t, s^t, -s */
)
/* Then we have no args to print, so go ahead and display u */
{ //flag = 0; /* or maybe 1 if raised exp is required */
svg_display(u,x,(coord)(y + level - LEVEL(u)),attr);
x = (coord)(x + WIDTH(u));
flag = 0;
}
else /* it's not so simple, u = f(x) and we must display f */
{ flag = 2; /* or maybe 3 if raised exp is required */
fname[0] = f;
fname[1] = '\0';
svg_textstyle = ITALIC;
x += 2; // otherwise f sticks out to the left and there's extra space on the right
svgwrite(fname,x,(coord)(y+level-get_charspace()/2),SIZE(newexpr),rgb);
x += svg_text_width(fname,SIZE(newexpr));
}
/* Now, either write n primes or a raised exponent if necessary */
/* careful about the y-coordinate of the primes or exponent; e.g.
if printing (a/b)' the prime does not go at the water level of (a/b)
but rather at the top */
if(BB_OBJECT(exp) &&
*( primestring = *((char **) (ARGPTR(exp)+1))) == '\xE2'
&& primestring[1] == '\x80' && primestring[2] == '\xB2'
// so primestring points to the UTF code for the 'prime' symbol
)
{ if(flag) /* e.g. f'(a/b) */
{ svg_textstyle = SYMBOLFONT;
svgwrite((char *) primestring,(coord)(x),(coord)(y+level-get_charspace()/2-3),SIZE(newexpr),rgb);
x+= WIDTH(ARG(2,newexpr));
// ARG(2,newexpr) is an artificial atom whose printstring is a
// sequence of UTF-8 prime symbols.
}
else /* e.g. (a/b)' */
{ svg_textstyle = SYMBOLFONT;
svgwrite((char *) primestring,x,y,SIZE(newexpr),attr);
}
}
else /* raised exponent */
{ ++flag; /* now flag is set to the proper case number */
if(flag==3) /* e.g. prime(f(a/b),8) */
svg_display(exp,x,(coord)(y+level-6-get_charspace()/2),attr);
else /* e.g. prime(a/b,8) */
svg_display(exp,x,y,attr);
x += WIDTH(exp);
}
if(flag == 2) /* have args to write, no raised exponent */
{ svg_write_args(u,x,y,attr);
return;
}
if(flag == 3) /* args and a raised exponent */
{ if(LEVEL(u) == level) /* u does not stick out above the exponent */
svg_write_args(u,x,y,rgb);
else
svg_write_args(u,x,(coord)(y + level-LEVEL(u)),rgb);
}
}
/*______________________________________________________________________*/
/* lim(x->0,f(x)) or lim(x->0,LEFT,f(x)) or lim(x->0,RIGHT,f(x)) */
static void svg_display_limit(term newexpr, coord x, coord y, int attr)
{ unsigned short arity = (unsigned short)(ARITY(newexpr)-1);
term arg;
term dir = newexpr; // initialize to prevent a warning, doesn't matter how
coord s;
if (arity ==2)
arg = ARG(2,newexpr);
else
{ arg = ARG(3,newexpr);
dir = ARG(2,newexpr);
}
if(COLOR(newexpr))
{ HIGHLIGHT(ARG(1,newexpr));
HIGHLIGHT(ARG(2,newexpr));
if(arity == 3)
HIGHLIGHT(ARG(3,newexpr));
}
s = svg_write_lim(ARG(1,newexpr),arity,dir,x,(coord)(y+LEVEL(newexpr))-1,SIZE(newexpr),attr);
svg_display(arg,s,y,attr);
}
/*________________________________________________________________*/
static coord svg_write_lim(term subscript, int arity, term dir, coord x, coord z, US sizein, int attr)
/* write "lim" and the subscript. The integer arity is 2 or 3, 2 for
a two-sided limit, 3 for a one-sided limit; if arity is 3 then dir is
the direction argument, left or right; x and z are the papyrus
coordinates of the left-hand side and water level of the term;
attr is the color bit. The return value is x plux the papyrus-coordinate
width used up in writing "lim" and the subscript plus a space,
i.e. the x-coordinate to use for displaying the limitand.
*/
{ int halfheight;
int limwidth = functor_width(LIMIT, sizein);
int rgb; // color to use in writing the sqrt
if(attr)
rgb = attr;
else if(COLOR(subscript))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
coord charwidth = (coord)(sizein == TENPOINTHEIGHT ? 12 : 9);
coord s, subscriptwidth;
/* compute the x-coordinate where "lim" should be written */
subscriptwidth = (coord) WIDTH(subscript);
/* not counting the + or - in a one-sided limit */
if(subscriptwidth < limwidth)
s=x;
else
s = (coord)(x + (subscriptwidth - limwidth)/2);
svg_textstyle = ROMAN;
halfheight = (coord)(sizein == TENPOINTHEIGHT ? 11 : 8);
svgwrite("lim",s,(coord)(z-halfheight),sizein,rgb);
s = (coord)(s + limwidth); /* next place after "lim" */
svg_display(subscript,x,(coord)(z+halfheight),rgb);
if(x + subscriptwidth > s)
s= (coord)(x+subscriptwidth);
if(arity==3)
{ char sign = (char) (FUNCTOR(dir)==LEFT ? '-' : '+');
svg_textstyle = SYMBOLFONT;
svgputch(sign,s,z+halfheight,EIGHTPOINT,rgb);
s = (coord)(s + 3); /* not get_charspace() because we are in EIGHTPOINT */
}
s = (coord)(s + charwidth); /* the blank space mentioned in bblock_limit */
/* put in a blank whether or not arity == 3 */
return s;
}
/*______________________________________________________________________*/
static void svg_display_sqrt(term newexpr, coord x,coord y, int attr)
{ term arg;
coord xarg;
coord height = HEIGHT(newexpr);
coord width = (coord) WIDTH(newexpr);
arg = ARG(1,newexpr);
int rgb; // color to use in writing the sqrt
if(attr)
rgb = attr;
else if(COLOR(newexpr))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
if(COLOR(newexpr))
HIGHLIGHT(arg);
svg_draw_sqrt(width,height,x,y,rgb);
xarg = x + get_sqrtspace() + get_undersqrtshift();
svg_display(arg,xarg,(coord)(y+1+get_belowrootbar()),attr);
/* spaces for the sqrt sign, then the arg, shifted left a bit */
}
/*___________________________________________________________________*/
static void svg_display_root(term newexpr, coord x,coord y, int attr)
/* Display the index at the left side just above the 'hook' of
the root sign. Leave 'get_rootspace()'
units free and then draw the square root */
{ term arg,index;
coord height = HEIGHT(newexpr);
coord argheight,argwidth;
arg = ARG(2,newexpr);
coord waterlevel = LEVEL(arg);
index = ARG(1,newexpr);
int rgb; // color to use in writing the sqrt
if(attr)
rgb = attr;
else if(COLOR(newexpr))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
if(COLOR(newexpr))
HIGHLIGHT(arg);
coord topofindex = (coord)(y + waterlevel + get_belowrootbar() - HEIGHT(index))+2;
svg_display(index,x,topofindex,attr);
x = (coord)(x + WIDTH(index) + get_rootspace());
argheight = HEIGHT(arg);
y = (coord)(y + height - argheight - get_belowrootbar());
argwidth = (coord)(WIDTH(arg) + 2*get_sqrtspace() - get_undersqrtshift());
svg_draw_sqrt(argwidth,(coord)(argheight+get_belowrootbar()),x,y,rgb);
svg_display(arg,(coord)(x+get_sqrtspace()+get_undersqrtshift()),(coord)(y+get_belowrootbar()),attr);
}
/*___________________________________________________________________*/
static void svg_display_subscripted_function(term newexpr, coord x,coord y, int attr)
{ term index;
term dummy;
int rgb;
if(attr)
rgb = attr;
else
rgb = COLOR(newexpr)? get_highlightcolor() : get_textcolor();
block b;
unsigned short f = FUNCTOR(newexpr);
char fname[32];
int i;
coord level = LEVEL(newexpr);
coord depth;
index = ARG(1,newexpr);
functor_string(f,SCREEN,fname); /* in terms.c */
if(f == POLYGAMMA)
svg_textstyle = SYMBOLFONT;
else
svg_textstyle = ITALIC;
svgwrite(fname,x,(coord)(y+level-get_charspace()/2),SIZE(newexpr),rgb);
x = (coord)(x + functor_width(f,(unsigned short)SIZE(newexpr)));
svg_display(index,x,(coord)(y+level),rgb);
if(ARITY(newexpr) == 2)
/* constant of integration with no parameters */
return; /* nothing more to do */
x = (coord)(x + WIDTH(index));
dummy = make_term(f,(unsigned short)(ARITY(newexpr)-1));
/* make a term like newexpr but without the index arg */
ARGREP(dummy,0,ARG(0,newexpr));
for(i=1;i<ARITY(dummy);i++)
{ ARGREP(dummy,i,ARG(i+1,newexpr));
}
b = GETBLOCK(dummy);
depth = 0;
for(i=1;i<ARITY(dummy);i++)
depth = (coord)(MAXIMUM(depth,HEIGHT(ARG(i,dummy)) - LEVEL(ARG(i,dummy))));
depth = (coord)(depth + level);
b.h = depth;
SETBLOCK(dummy,b);
svg_write_args(dummy,x,y,rgb);
RELEASE(dummy);
return;
}
/*___________________________________________________________________*/
static void svg_display_logb1(term newexpr, coord x, coord y, int attr)
{ term arg,index;
coord level = LEVEL(newexpr);
coord charheight = (coord)(SIZE(newexpr) == TENPOINT ? get_charspace() : get_eightpointheight());
arg = ARG(2,newexpr);
index = ARG(1,newexpr);
svg_textstyle = ROMAN;
svgwrite("log",x,(coord)(y+level-charheight/2),SIZE(newexpr),attr);
x = (coord)(x + functor_width(LOG,(unsigned short) SIZE(newexpr)) + get_powerspace());
svg_display(index,x,(coord)(y+level),attr);
x = (coord)( x + WIDTH(index) + get_productspace(SIZE(newexpr))); /* half a space after the index */
svg_display(arg,x,y,attr);
}
/*___________________________________________________________________*/
static void svg_display_eval(term newexpr, coord x, coord y, int attr)
{ term u,lo,hi;
block b = GETBLOCK(newexpr);
coord height = b.h;
coord h,s;
u = ARG(1,newexpr);
lo = ARG(3,newexpr);
hi = ARG(4,newexpr);
s = (coord)(y + b.wl - LEVEL(u)); /* put u on the water level line */
svg_display(u,x,s,attr);
x = (coord)(x + WIDTH(u) + get_productspace(b.size));
h = (coord)(height - (HEIGHT(lo) + HEIGHT(hi) - 2*EIGHTPOINTHEIGHT ));
/* h is the height of the eval bar */
/* Now find the top of the eval bar */
svg_draw_vert(h,x,(coord)(y+HEIGHT(hi)-EIGHTPOINTHEIGHT),0.75, attr);
int displacement = get_lowerlimx()+1;
// without the +1 it looks too close to the eval bar.
// lowerlimx is for integrals; apparently the integral sign's different shape makes a difference.
x = (coord)(x + displacement);
s = (coord)(y + height - HEIGHT(lo));
svg_display(lo,x,s,attr);
svg_display(hi,x,y,attr);
}
/*__________________________________________________________________*/
/* We only have to display the rows and columns, as the brackets have
already been displayed by the time we get here */
static void svg_display_matrix(int lineupflag,term newexpr,coord x,coord y,int attr)
{ term entry;
int i,j;
int color;
coord entryx,entryy,row,rowheight,rowlevel,temp;
unsigned short rows = (unsigned short)(ARITY(newexpr) - 2);
coord *columnwidths = COLUMNWIDTHS(newexpr,rows);
unsigned short columns = (unsigned short)(ARITY(ARG(1,newexpr))-1);
int rgb;
if(attr)
rgb = attr;
else if(COLOR(newexpr))
rgb = get_highlightcolor();
else
rgb = get_textcolor();
/* Now display the rows */
row = y;
for(i=0;i<rows;i++)
{ entryx = x; /* leftmost point */
rowlevel = LEVEL(ARG(i+1,newexpr));
rowheight = HEIGHT(ARG(i+1,newexpr));
color = COLOR(ARG(i+1,newexpr)) | COLOR(newexpr); /* color (bit) of this row */
if(rgb == 0 && color)
rgb = get_highlightcolor();
for(j=0;j<columns;j++)
{ entry = ENTRY(i+1,j+1,newexpr); /* i,j-th entry of matrix */
entryy = (coord)(row + rowlevel - (LEVEL(entry)));
/* at this point entryx is at the left side of the column */
temp = entryx;
if(lineupflag)
{ entryx = (coord)(entryx + columnwidths[j] - WIDTH(entry)); /* right justify */
if(j>0 && j<columns-2 &&
(FUNCTOR(entry)!=ILLEGAL) /* non-void term */
&& FUNCTOR(entry) != '-'
)
{ /* check whether all previous entries are void */
int p;
term priorentry;
for(p=0;p<j;p++)
{ priorentry = ENTRY(i+1,p+1,newexpr);
if(FUNCTOR(priorentry) != ILLEGAL) /* nonvoid*/
break;
}
if(p < j) /* this isn't the first nonvoid entry on this row */
{ svg_textstyle = SYMBOLFONT;
svgputch('+',(coord)(entryx - get_sumspace(TENPOINT) - get_charspace()),(coord)(entryy + LEVEL(entry) -get_signoffset(TENPOINT,'+')),SIZE(newexpr), color);
}
}
}
else
{
entryx = (coord)(entryx + (columnwidths[j]- WIDTH(entry))/2);
/* center the entry */
}
svg_display(entry,entryx,entryy, rgb);
entryx = (coord)(temp + columnwidths[j] + get_sumspace(TENPOINT));
/* this gets entryx ready for the next column */
}
row = (coord)(row + rowheight + get_matrixspace());
}
}
/*____________________________________________________________________*/
int text_style(unsigned f)
/* return SYMBOL, ROMAN, or ITALIC according to which fontsize is required
for writing the name of functor f. Only called for
functors which are not PREFIX or INFIX. */
{ switch(f)
{ case NOT:
case SUM:
case PRODUCT:
case ARROW:
case SEQ:
case APPROXEQ:
case EXISTS:
case ALL:
case GAMMA:
case LAM:
case DIGAMMA:
case POLYGAMMA:
case RIEMANNZETA:
return SYMBOLFONT;
case REALPART:
case IMAGPART:
case CIS:
case GCD:
case MAXFUNCTOR:
case MINFUNCTOR:
return ROMAN;
}
return ITALIC;
}
/*______________________________________________________*/
static void svg_display_long_atom(term expr, coord x, coord y, int attr)
/* display an atom whose print name is longer than one character */
{ char *s = *( (char **)(ARGPTR(expr)+1));
/* That's the print name in question */
int k =(int) strlen(s);
int charheight;
assert(k > 1);
if(!SUBSCRIPT(FUNCTOR(expr))) /* not a subscripted variable */
{ svgwrite(s,x,y,SIZE(expr),attr);
return;
}
/* Now it's a subscripted variable */
svg_textstyle = ITALIC;
charheight = (coord)(SIZE(expr) == TENPOINTHEIGHT ? get_charspace() : get_eightpointheight());
char temp[3];
temp[0] = s[0];
temp[1] = '\0';
svgwrite(temp,x,y-2,SIZE(expr),attr);
// I don't know why this -2 is needed, but it works!
svg_textstyle = ROMAN;
int deltax = (int)svg_text_width(temp,SIZE(expr));
int deltay = HEIGHT(expr) - charheight;
svgwrite(s+1,(coord)(x+deltax),(coord)(y+deltay),EIGHTPOINT,attr);
}
/*_______________________________________________________________________*/
static void svg_display_binder(term newexpr,coord x,coord y,int attr)
/* Display LAMBDA, FORALL, EXISTS */
{ term u,v;
coord level = LEVEL(newexpr);
unsigned short f = FUNCTOR(newexpr);
char fname[32];
int i;
functor_string(f,SCREEN,fname); /* in dispfunc.c */
u = ARG(1,newexpr);
v = ARG(2,newexpr);
svg_textstyle = text_style(f);
svgwrite(fname,x,(coord)(y+level-get_charspace()/2),SIZE(newexpr),attr);
x = (coord)(x + functor_width(f,(unsigned short)SIZE(newexpr)));
if(FUNCTOR(u) == AND)
{ for(i=1;i<ARITY(u);i++)
{ svg_display(ARG(i,u),x,(coord)(y+level-LEVEL(u)),attr);
x = (coord)(x + WIDTH(ARG(i,u)));
}
}
else
{ svg_display(u,x,(coord)(y+level-LEVEL(u)),attr);
x = (coord)(x + WIDTH(u));
}
svg_display(v,x,y,attr); /* includes its own parentheses if necessary */
return;
}
/*_______________________________________________________________________*/
coord save_space(term expr)
/* return the amount of space between the left side of the parens
and the start of the parenthesized bblocked term expr, in papyrus coordinates.
This must stay in sync with strip_parens in lterm.c; if you change one,
change the other to match.
*/
{ unsigned short f = FUNCTOR(expr);
block b = GETBLOCK(expr);
if(f == ABSFUNCTOR)
/* This part must match bblock_abs */
return 1 + get_parenspace(b.size); /* bars are only 1 unit wide */
if(
BB_OBJECT(expr) || /* put parens almost right next to numbers, using only get_parenspace()/2 */
(f=='-' && BB_OBJECT(ARG(1,expr))) ||
(f=='-' && b.size == EIGHTPOINT)
)
return (coord)(get_parenwidth(b.size) + get_parenspace(b.size)/2);
else /* but otherwise leave a space */
return (coord)(get_parenwidth(b.size) + get_parenspace(b.size));
}
/*______________________________________________________*/
static int cxChar=16, cyChar=16;
// get rid of these and functions below after line breaks no longer use them.
/*_______________________________________________________*/
long xpixel_to_papyrus(long x)
/* convert horizontal pixel coordinates to papyrus coordinates */
{ long ans;
ans = (long) ((x << 4) / cxChar);
if(cxChar * ans < (x << 4) - cxChar/2) /* round up if necessary */
++ans;
return ans;
}
/*_______________________________________________________*/
long ypixel_to_papyrus(long x)
/* convert vertical pixel coordinates to papyrus coordinates */
{
long ans = (int) (( x << 4) / cyChar);
if(cyChar * ans < (x << 4) - cyChar/2) /* round up if necessary */
++ans;
return (long) ans;
}
/*_______________________________________________________*/
long papyrus_to_xpixel(long x)
/* convert papyrus coordinates to horizontal coordinates */
/* although papyrus coordinates of points on the papyrus are
always positive, sometimes this is applied to a difference of
such coordinates which can be negative.
*/
{ long ans;
unsigned long temp;
if(x<0)
return -papyrus_to_xpixel(-x);
temp = ((unsigned long)(x)) * cxChar;
ans = (long)(temp >> 4);
if(temp - (ans << 4) >= 8)
++ans; /* round up if necessary */
return ans;
}
/*_______________________________________________________*/
long papyrus_to_ypixel(long y)
/* convert papyrus coordinates to vertical pixel coordinates */
/* although papyrus coordinates of points on the papyrus are
always positive, sometimes this is applied to a difference of
such coordinates which can be negative.
*/
{ long ans;
unsigned long temp;
if(y < 0)
return -papyrus_to_ypixel(-y);
temp = ((unsigned long)y) * cyChar;
ans = (long)(temp >> 4);
if(temp - (ans << 4) >= 8)
++ans; /* round up if necessary */
return ans;
}
#if 0
// WebMathXpert knows nothing about pixels; that's the interfsces's job.
void measure(term t, long* x, long *y)
/* Compute the size of a bblocked term t in pixels */
/* papyrus_to_xpixel will use the static cxChar in wdisplay.c,
which is set by CreateDisplayFonts in WinMain
*/
{ *x = papyrus_to_xpixel((int)WIDTH(t));
*y = papyrus_to_ypixel(HEIGHT(t));
}
#endif
/*______________________________________________________________*/
unsigned short atom_width(unsigned short f, short pointsize)
/* pointsize is TENPOINT or EIGHTPOINT. Return the
width in papyrus coordinates required to display the
predefined atom or single-letter variable with functor f.
*/
{ term u;
assert(('a' <= f && f <= 'z') ||
('A' <= f && f <= 'Z') ||
f == '?' ||
PREDEFINED_ATOM(f)
);
SETFUNCTOR(u,f,0);
return (unsigned short) svg_text_width(atom_string(u),pointsize);
}
/*_________________________________________________________________________*/
static void svg_vert_aux(unsigned short h, coord x, coord y, int attr, char* top, char *middle, char *bottom, double midshift, US sizein)
/* draw an integral, by writing 'top' at the
papyrus coords (x,y), 'middle' several times vertically, and 'bottom'
at (x,y+h-16). The middle character is shifted right by midshift.
Use the highlight color if attr is nonzero, else use the
text color.
top, middle, bottom are sometimes UTF-8 (e.g. for integral signs) so must be strings,
not just characters.
*/
/* There is some 'internal leading' blank space in some
of the vertical bars used as 'middle', so you get
gaps if you write them vertically every 16 units.
They must overlap a bit. */
{
svg_textstyle = SYMBOLFONT;
svgwrite(top, x,y,sizein,attr);
svgwrite(bottom,x,(coord)(y+h-16),sizein,attr);
int vertical_shift = (sizein == TENPOINT ? 12 : 8);
svg_draw_vert(h-24,x+midshift,y+vertical_shift,0.75,attr);
svg_draw_vert(h-24,x+midshift+0.5,y+vertical_shift,0.75,attr);
}
/*_________________________________________________________________________*/
static void svg_vert_aux2(unsigned short h, coord x, coord y, int attr, char *top, char *middle, char *bottom, char *center, US sizein,US dir)
/* draw a large left or right brace, by writing 'top' at the
papyrus coords (x,y), 'bottom' at (x,y+h-16), 'center' in the middle,
and filling in with 'middle'. They have to be strings because they use UTF-8 characters. */
/* h can be less than 32, for example h=22 on x^2+2.
When h == 32, a gap can still occur between the upper and
lower character for some magnification values, e.g. when
23 pixels == 16 coords, a gap is observed between the upper
and lower integral signs, which do mesh perfectly at other
magnifications. This is a mystery. Finally I just decided
to write the vertical bars every HALF get_charspace() from top
to bottom. That will certainly eliminate any gaps!
*/
{ int i,n,extra;
svg_textstyle = SYMBOLFONT;
svgwrite(top, x,y, sizein,attr);
svgwrite(bottom,x,(coord)(y+h-16),sizein,attr);
svgwrite(center,x,(coord)(y+h/2-8),sizein,attr);
// assert(h >= 48); // h == 40 on cases( if(t < 5, x-4), if(t >= 5,2-x))
n = (h-32)/16 ; /* number of vertical bars required above center; they will overlap,
each one covering 8 new coord units and overlapping by 8. */
if(n == 0)
return; // no vertical bars needed.
extra = h-48 - n; // corrected 1.21.25
if (dir == 0)
x += 2;
else
x -= 2;
for(i=0;i<n;i++)
svgwrite(middle,x,(coord)(y+8*(i+1)),sizein,attr);
for(i=0;i<n;i++)
svgwrite(middle,x,(coord)(y+h-(i+3)*8),sizein,attr);
if(extra)
/* fraction of another vertical bar is needed */
{ svgwrite(middle,x,(coord)(y+h/2-20),sizein,attr);
svgwrite(middle,x,(coord)(y+h/2+4), sizein,attr);
}
if(extra > 6) // changed from 8 to 6 on 1.9.00
{ svgwrite(middle,x,(coord)(y+h/2-24),sizein,attr);
svgwrite(middle,x,(coord)(y+h/2+8),sizein,attr);
}
}
/*_________________________________________________________________________*/
static void svg_draw_integral(unsigned short h,coord x,coord y,US sizein,int attr)
/* draw an integral sign centered in the character space column whose
upper left corner is at x,y; make it h units high (a unit is 1/16 character).
Use the highlight color if attr is nonzero, else use the
text color
This is used even if the integrand is just one line high; we never use
the single-glyph integral sign. Neither does \TeX.
*/
{
double shift = 4.54; // for TENPOINT
if (sizein == EIGHTPOINT)
{ shift = 3.25;
y += 3;
}
svg_vert_aux(h,x,y,attr, "⌠","⎮","⌡",shift,sizein); /* integral top, bar, bottom */
}
/*_________________________________________________________________________*/
static void svg_display_bra( unsigned short b, /* b = PARENS, BRACES, BARS or BRACKETS */
unsigned short h, /* desired height, in coord units */
coord x, /* x-coordinate in window */
coord y, /* y-coordinate in window */
int attr, /* colors to use; nonzero for highlighted terms */
unsigned short dir, /* 0 for open paren, 1 for close paren */
unsigned short sizein
)
/* Draw a (possibly) big parenthesis, braces, bar, or bracket.
Use the highlight color if attr is nonzero, else use the
text color. */
{ char *top, *bottom, *middle, *vertical;
int textstyle = svg_textstyle;
svg_textstyle = SYMBOLFONT;
if(h <= get_charspace() + 7 && b == PARENS)
{ svg_textstyle = SYMBOLFONT;
assert(0); // this case is handled directly
return;
}
double howthick = sizein == EIGHTPOINT ? 0.72 : 1.2;
double openoffset = sizein == EIGHTPOINT ? 1.06 : 1.66;
double closeoffset = sizein == EIGHTPOINT ? 0.05: 1.58;
double yoffset = sizein == EIGHTPOINT ? 7 : 13;
if(b == PARENS && dir == 0)
{ /* big open parens */
top = "⎛";
bottom = "⎝";
svg_textstyle = SYMBOLFONT; // print the parens in roman type, not italic
if(h <= 26)
svgwrite(top, x,y-3,sizein,attr);
else
svgwrite(top, x,y,sizein,attr);
svgwrite(bottom,x,(coord)(y+h-17),sizein,attr);
if(h > 26)
svg_draw_vert(h-26,x+openoffset,y+yoffset,howthick,attr);
svg_textstyle = textstyle;
return;
}
else if(b == PARENS && dir == 1)
{ /* big close parens */
top = "⎞";
bottom = "⎠";
svg_textstyle = SYMBOLFONT;
if(h <= 26)
svgwrite(top, x,y-3,sizein,attr);
else
svgwrite(top, x,y,sizein,attr);
svgwrite(bottom,x,(coord)(y+h-17),sizein,attr);
if(h > 26)
svg_draw_vert(h-26,x+ get_parenwidth(sizein)-closeoffset,y+yoffset,howthick,attr);
svg_textstyle = textstyle;
return;
}
else if(b==BRACKETS && dir == 0)
{ /* open brackets */
// top = "┌" ; // "⎡";
// bottom = "└"; //"⎣";
// svgwrite(top, x-5,y-8,attr);
// svgwrite(bottom,x-5,y+h-8,attr);
svg_draw_vert(h,x,y,0.75,attr);
svg_draw_horz(get_parenwidth(sizein),x,y,attr);
svg_draw_horz(get_parenwidth(sizein),x,y+h,attr);
svg_textstyle = textstyle;
return;
}
else if(b==BRACKETS && dir == 1)
{ // top ="┐"; // "⎤";
// bottom = "┘"; // "⎦";
// svgwrite(top, x,y-8,attr);
// svgwrite(bottom,x,(coord)(y+h-8),attr);
svg_draw_vert(h,x+ get_parenwidth(sizein),y,0.75,attr);
svg_draw_horz(get_parenwidth(sizein),x+get_parenwidth(sizein)-get_parenwidth(sizein),y,attr);
svg_draw_horz(get_parenwidth(sizein),x+get_parenwidth(sizein)-get_parenwidth(sizein),y+h,attr);
svg_textstyle = textstyle;
return;
}
else if(b == BRACES && dir == 0)
{ top = "⎧";
middle = "⎨";
vertical = "⎢";
bottom = "⎩";
svg_vert_aux2(h,x,y,attr,top,vertical,bottom,middle,sizein, dir);
return;
}
else if(b == BRACES && dir == 1)
{ top = "⎫";
middle = "⎬";
bottom = "⎭";
vertical = "⎢";
svg_vert_aux2(h,x,y,attr,top,vertical,bottom,middle,sizein,dir);
return;
}
else if(b== BARS)
{ svg_draw_vert(h,x,y,0.75,attr);
return;
}
else
assert(0); /* all cases covered */
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists