Sindbad~EG File Manager
//
// sendDocument.c
// for Web MathXpert
//
/* Created by Michael Beeson on 2/25/24.
2.29.24 modified sendGraphDocument to send the graph titles
3.17.24 added sendParameters and called it in sendGraphDocument.
3.23.24 added call to draw_inequality2
3.23.24 made sendGraphDocument take only one argument
3.28.24 added calls to begin_svg and end_svg in sendGraphDocument
4.7.24 added code to highlight selected term
5.7.24 changed code for nexty in sendSymbolDocument
5.15.24 modified sendSymbolDocument to draw the selected rectangle when nexty is known
and give it the right location.
6.9.24 corrected sendSymbolDocument to increment outbuffer etc. after sendErrors and sendComments
6.10.24 added code near SELECTED in sendSymbolDocument
7.5.24 added 'padding' before PixelRect draws the title rectangle.
7.7.24 corrected ndcToPixel
7.12.24 and again
7.26.24 and again
7.26.24 added sendAuthorsCommentary
7.31.24 added sendToolTips and corrected sendAssumptions, which was probably sending only
one assumption because of omitting ++outbuffer.
8.10.24 removed code to print TitleRect; interface will govern title display and titlebackground
8.12.24 changed label and label2 to 32 bytes in sendDocument to avoid warning
8.13.24 added code to send the document number in sendGraphDocument and sendSymbolDocument
8.23.24 added a line at the end of sendToolTips to send Javascript defining graphtypeNumber.
9.5.24 added set_anddisplay around make_title to remove matrix bars
9.10.24 added createPrimeParameter
9.10.24 made sendSingularities do nothing on ODE problems
9.11.24 modfied AdjustToolbar.js to not display jumps, singularities, assumptions buttons
on ODE, ODE2, etc.
9.12.24 corrected sendSymbolDocument where it sends Javascript, search for "script"
9.14.24 added sendToolTips in sendSymbolDocument
9.15.24 added code at the beginning of sendGraphDocument to write javascript to set language.
9.30.24 sendComments was sending them with class "mathError" instead of class "comment"
11.3.24 removed unnecessary lines just before sending title.
11.3.24 added code to send a warning to PHP if draw returns nonzero.
11.23.24 modified send_comments to send the permanent comments as well as temporary ones
12.5.24 wrote termtoSVGElementNoPadding and called it in sendParameters
12.9.24 modified percent calculation for the case totalsteps == 0.
12.10.24 and again, to require totalsteps > 2.
12.16.24 made process_setupAndCheckSymbol also return Javascript to set the topic.
1.20.25 modified sendParameters on HDE problems.s
1.20.25 added code to not send an error for more than 10000 steps in Runge-Kutta.
1.21.25 added code to hide selectTermHint when any step is taken, not just a manual step.
2.1.25 added code to sendComments to put more comments at a greater y.
2.17.25 added code to increment nexty after sendComments and sendErrors
1.17.25 added code to send Javascript defining activeline
2.18.25 changed id[16] to id[32] to avoid a warning
3.6.25 made sendJavaScriptGraphs send activeParameter
3.8.25 removed code in sendGraphDocument that called sendTitle with parameter labels.
3.8.25 added tooltips for the parameterSliders
3.9.35 added assert( g->activeparameter < g->nparameters)
*/
#include <stdio.h>
#include <assert.h>
#include <math.h> // fabs
#include "uthash.h"
#include "heap.h"
#include "heaps.h"
#include "terms.h"
#include "parser.h"
#include "pcontrol.h"
#include "defns.h" // needed by vaux
#include "vaux.h" // vardata
#include "proverdl.h"
#include "model.h"
#include "arith.h"
#include "polyval.h"
#include "graphstr.h"
#include "probtype.h"
#include "coord.h"
#include "termtoSVG.h"
#include "mpdoc.h"
#include "ProcessMessage.h"
#include "ProblemLibrary.h"
#include "english.h" // english
#include "docdata.h" // init_doc_data
#include "natlang.h" // set_language
#include "display1.h" // TENPOINT
#include "autostep.h" // autostep
#include "bigrect.h" // BIGRECT
#include "lterm.h" // ltermlist
#include "papyrus.h" // init_papyrus
#include "undo.h" // undo
#include "svgSymbolText.h"
#include "natlang.h" // get_language_number
#include "subjectstr.h" // NSUBJECTS
#include "svgGraph.h" // set_svgDevice
#include "draw.h" // draw
#include "grapher.h" // preparetograph
#include "sendDocument.h" // functions defined in this file
#include "wtitle.h" // make_title
#include "grafineq.h" // draw_inequality2
#include "rgb.h"
#include "heap.h"
#include "heaps.h"
#include "activedoc.h"
#include "mpmem.h" // permcopy
#include "display.h" // WIDTH, HEIGHT
#include "hovertext.h" // getTooltips
#include "automode.h" // get_activeline
static void sendJavaScriptGraph(PDOCDATA pDocData, char *outbuffer, int outbuffersize);
static void sendAuthorsCommentary(PDOCDATA pDocData, char *outbuffer, int outbuffersize)
// send the author's commentary, if any, as an <svg> element with display:block
// and class "commentary" and id "remarks"
// We did not store the commentary in pDocData (as it's rarely used and might
// be long). But it's easy to fetch it again.
{
char problemtext[2048];
char ranges[1000];
char commentary[COMMENTARYBUFLENGTH];
int topicnumber = pDocData->currenttopic;
int source = pDocData->problemsource;
if (source != SOURCE_MATHPERT)
return; // there won't be a commentary to send.
int problemnumber = pDocData->problemnumbers[SOURCE_MATHPERT];
int err = getStoredProblem(topicnumber,problemnumber,problemtext,ranges, commentary);
if(err || !strcmp(commentary, ""))
return; // no commentary to send
svgSymbolTextInvisibleElement(commentary, outbuffer, "remarks", "commentary", outbuffersize, 0,0,0); // black, (0,0)
printf("sent author's commentary\n");
}
/*__________________________________________________________________________________*/
static void sendAssumptions(PDOCDATA pDocData, char *outbuffer, int outbuffersize)
// send the assumptions, if any, as <svg> elements with display:none
{
assumption ** assumptionlist;
proverdata q = pDocData->DocProverData;
assumptionlist = q.assumptions;
int nassumptions = q.nextassumption;
char *next = outbuffer;
int byteswritten;
coord y = 0;
char id[32];
for(int k=0;k<nassumptions;k++)
{ term t = assumptionlist[k]->prop;
int level, width, height, color;
color = 0; // black for assumptions
snprintf(id,32,"assumption%d",k);
termtoInvisibleSVGElement(t, next, id , "assumption", outbuffersize,0, y, color, &level, &width, &height);
y += height + 16;
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffer += byteswritten;
outbuffersize -= byteswritten;
// printf("sent assumption %d\n", k);
}
}
/*_____________________________________________________________*/
static void sendToolTips(PDOCDATA pDocData, char *outbuffer, int outbuffersize)
// send the tooltips as <svg> elements with display:none and
// ids like "tooltip-draw" for the "draw" button.
// The class doesn't matter; here I make it "tooltip"
// Also, send pDocData->mainchoice as a Javascript variable graphtypeNumber,
// which is needed by AdjustToolbar.js
{
int ntooltips;
tooltip *tooltips = getTooltips(pDocData->problemtype, &ntooltips);
char *next = outbuffer;
int byteswritten;
int k;
for(k=0;k<ntooltips;k++)
{ tooltip t = tooltips[k];
char id[128];
strcpy(id, "tooltip-");
strncat(id,t.id,120);
svgSymbolTextInvisibleElement((char *)t.text, outbuffer, (char *)id, "tooltip", outbuffersize,0, 0, 0); // black, 0,0
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffer += byteswritten;
outbuffersize -= byteswritten;
// printf("sent tooltip %d\n", k);
}
// Now tooltips for the dynamically created parameter buttons
int nparameters = pDocData->DocVarData.nparameters;
for(k = 0; k<nparameters;k++)
{ // send a tooltip with parameter "parameterButtonk"
// and a tooltip with parameter "parameterIncrementButtonk"
// and a tooltip with parameter "parameterDecrementButtonk"
char buf1[64];
char buf2[64];
char buf3[64];
char buf4[64];
sprintf(buf1,"tooltip-parameterButton%d",k);
sprintf(buf2,"tooltip-parameterIncrementButton%d",k);
sprintf(buf3,"tooltip-parameterDecrementButton%d",k);
sprintf(buf4,"tooltip-parameterSlider%d",k);
char *tip1 = (char *) english(1478); /* Set parameter value or increment */
char *tip2 = (char *)english(1476); /* Increase parameter value */
char *tip3 = (char *)english(1477); /* Decrease parameter value */
char *tip4; // tooltip for sliders
if(pDocData->DocVarData.nparameters > 1)
tip4 = (char *)english(11);
/* "You can use the green slider; click a white slider (slowly) to turn it green."*/
else
tip4 = (char *) english(12);
/* "Slider changes the graph immediately"*/
svgSymbolTextInvisibleElement(tip1, outbuffer, buf1, "tooltip", outbuffersize,0, 0, 0);
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffer += byteswritten;
outbuffersize -= byteswritten;
svgSymbolTextInvisibleElement(tip2, outbuffer, buf2, "tooltip", outbuffersize,0, 0, 0);
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffer += byteswritten;
outbuffersize -= byteswritten;
svgSymbolTextInvisibleElement(tip3, outbuffer, buf3, "tooltip", outbuffersize,0, 0, 0);
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffer += byteswritten;
outbuffersize -= byteswritten;
svgSymbolTextInvisibleElement(tip4, outbuffer, buf4, "tooltip", outbuffersize,0, 0, 0);
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffer += byteswritten;
outbuffersize -= byteswritten;
}
}
/*_________________________________________________________________*/
int sendErrors(PDOCDATA pDocData, char *outbuffer, int outbuffersize, int y)
// returns the number of lines of error message to be written
{
char error[ERRBUFLENGTH];
int i;
for(i = 0; i< DIM_ERROR_BUFFER;i++)
{ strncpy(error,pDocData->papyrus->error_buffer[i],ERRBUFLENGTH);
if(error[0] == '\0')
break; // no more error lines
int color = RGB(255,0,0);
char id[16];
sprintf(id,"error%d",i);
svgSymbolTextElement(error, outbuffer,id,"mathError", outbuffersize, color,0, y+16*i);
int byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
}
return i;
}
/*_________________________________________________________________*/
static int sendComments(PDOCDATA pDocData, char *outbuffer, int outbuffersize, int y)
// this sends the "temporary" comments that are not embedded in pDocData
// with ids like comment0, comment1,...
// return value is the number of content lines written
{
char comment[COMMENTBUFLENGTH];
int i;
char id[16];
// first the temporary comments
for(i = 0; i< DIM_COMMENT_BUFFER;i++, y+=16)
{ strcpy(comment,pDocData->papyrus->comment_buffer[i]);
if(comment[0] == '\0')
break; // no more comment lines
if(comment[0] == '!') // "!" marks a permanant comment, embedded in pDocData
continue; // don't send permanent comments, they are send when their line is sent.
int color = get_textcolor();
sprintf(id,"comment%d",i);
svgSymbolTextElement(comment, outbuffer,id,"comment", outbuffersize, color,0, y);
int byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
}
return i;
}
/*_________________________________________________________________*/
static void sendProgressElements(PDOCDATA pDocData, char *outbuffer, int outbuffersize)
// send the progress elements, if any, as <svg> elements with display:none
// after they are sent, free the memory that held them and set pDocData->nextProgress to 0.
// If the send fails somehow then what will happen?
{
char *next = outbuffer;
int byteswritten;
coord y = 0;
if(pDocData->progress == NULL)
return; // nothing to do
int color = pDocData->DocDisplayControlData.textcolor;
char id[32];
for(int k=0;k<pDocData->nextProgress;k++)
{ term t = pDocData->progress[k];
int level, width, height;
snprintf(id,32,"progress%d",k);
termtoInvisibleSVGElement(t, next, id , "progress", outbuffersize,0, y, color, &level, &width, &height);
// they all get y = 0; Javascript can change if desired.
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffersize -= byteswritten;
outbuffer += byteswritten; // so we can write the next elements
printf("sent progress element %d\n", k);
}
free(pDocData->progress);
pDocData -> progress = NULL;
// Now we still need to send pDocData->progressTitle;
svgSymbolTextInvisibleElement((char *)pDocData->progressTitle, outbuffer,"progressTitle0", "progressTitle", outbuffersize, 0, 0, 0); // (x,y) = (0,0); color is black
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffersize -= byteswritten;
// don't bother incrementing outbuffer as this was the last thing to write and outbuffer is local
pDocData->nextProgress = 0;
}
/*__________________________________*/
#define PROGRESS_BUFFERSIZE 1024
int display_progress(term t, int titlenumber)
/* english(titlenumber) might be "Trying factors".
This is used when trying to factorize, or to integrate by parts.
t is one of the terms being tried. These will accumulate in
pDocData->progress, and be sent to the browser by
sendSymbolDocument(). When they are sent, pDocData->progress
should be freed and pDocData->nextprogress set to 0.
The return value in WebMathXpert is meaningless. (In Windows,
the user could terminate the search.)
*/
{
PDOCDATA pDocData = GetActiveDoc();
if(pDocData->progress == NULL)
{ pDocData->progress = calloc(PROGRESS_BUFFERSIZE,sizeof(term));
if(pDocData->progress==NULL)
assert(0);
pDocData->nextProgress = 0; // should be so already
pDocData->progressTitle = english(titlenumber);
}
permcopy(t,&pDocData->progress[pDocData->nextProgress]);
++pDocData->nextProgress;
return 0;
}
/*_________________________________________________________________*/
static void sendSingularities(PDOCDATA pDocData, char *outbuffer, int outbuffersize)
// send the singularities of the graphs.
// They are sent as invisible svg elements of class "singularity" and
// ids like singularity0~1, meaning, singularity1 of graph0.
// The text of the singularities of graph g is colored with g->graphcolor.
{ int ngraphs = pDocData->ngraphs;
int i,k,nsing;
int level, width, height;
char id[32];
coord y = 0;
char *next = outbuffer;
int byteswritten;
int graphtype = pDocData->graphproblemtype;
if(graphtype == ODE || graphtype == ODE2 || graphtype == ODESYSTEM || graphtype == HDE)
return; // slist is used for something else, no singularities are calculated.
for(i=0;i<ngraphs;i++)
{ double *nums = pDocData->graphs[i]->singularities;
nsing = pDocData->graphs[i]->nsingularities;
term *slist = pDocData->graphs[i]->slist;
int dimslist = pDocData->graphs[i]->dimslist;
unsigned color = pDocData->graphs[i]->graphcolor;
for(k=0;k<dimslist;k++)
{
snprintf(id,32,"singularity%d~%d",i,k);
termtoInvisibleSVGElement(slist[k], next, id ,"singularity",
outbuffersize,0, y, color, &level, &width, &height);
y += height + 16;
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffersize -= byteswritten;
}
for(k=0;k<nsing;k++)
{ term t = make_double(nums[k]);
snprintf(id,32,"singularity%d~%d",i,k+dimslist);
termtoInvisibleSVGElement(t, next, id ,"singularity",
outbuffersize,0, y, color, &level, &width, &height);
y += height + 16;
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffersize -= byteswritten;
}
}
}
/*_________________________________________________________________*/
static void sendJumps(PDOCDATA pDocData, char *outbuffer, int outbuffersize)
// send the jumps of the graphs.
// They are sent as invisible svg elements of class "jump" and
// ids like jump0~1, meaning, singularity1 of graph0.
// The text of the jumps of graph g is colored with g->graphcolor.
{ int ngraphs = pDocData->ngraphs;
int i,k,njumps;
int level, width, height;
char id[32];
coord y = 0;
char *next = outbuffer;
int byteswritten;
for(i=0;i<ngraphs;i++)
{ double *nums = pDocData->graphs[i]->jumps;
njumps = pDocData->graphs[i]->njumps;
term *jumplist = pDocData->graphs[i]->jumplist;
int dimjumplist = pDocData->graphs[i]->dimjumplist;
unsigned color = pDocData->graphs[i]->graphcolor;
for(k=0;k<dimjumplist;k++)
{
snprintf(id,32,"jump%d~%d",i,k);
termtoInvisibleSVGElement(jumplist[k], next, id ,"jump",
outbuffersize,0, y, color, &level, &width, &height);
y += height + 16;
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffersize -= byteswritten;
}
for(k=0;k<njumps;k++)
{ term t = make_double(nums[k]);
snprintf(id,32,"jump%d~%d",i,k+dimjumplist);
termtoInvisibleSVGElement(t, next, id ,"jump",
outbuffersize,0, y, color, &level, &width, &height);
y += height + 16;
byteswritten = (int) strlen(next);
next += byteswritten;
outbuffersize -= byteswritten;
}
}
}
/*__________________________________________________________________________*/
static void createPrimeParameter(char y,int nprimes, char *outbuffer,char *id)
/* Create an SVG element for a parameter name y with superscript of n primes and
subscript 0. We use UTF-8 symbols for the primes and subscript, producing
an output modeled on the example below, but with nprimes primes and using the
character passed as the first argument. Use 'size' for font-size in SVG.
Put the passed id in where "foo" occurs in the example.
Write the answer to outbuffer,
Here's the example:
<svg class="param" id="foo" width="29" height="41" xmlns="http://www.w3.org/2000/svg">
<text x="10" y="20" font-size="20">
<tspan style="font-style:italic;font-size:12pt";>y</tspan>
<tspan dy="5" dx="-5" font-size="12">₀</tspan>
<tspan dx="-11" dy="-10" font-size="12">′′′</tspan>
</text>
</svg>
*/
{ // Start of the SVG element
sprintf(outbuffer,
"<svg class=\"param\" id=\"%s\" width=\"29\" height=\"41\" viewbox=\"0 0 29 41\" xmlns=\"http://www.w3.org/2000/svg\">\n"
" <text x=\"8\" y=\"20\">\n"
" <tspan style=\"font-style:italic;font-size:16pt\";>%c</tspan>\n", id, y);
// Add the subscript ₀
strcat(outbuffer,
" <tspan dy=\"5\" dx=\"-5\" font-size=\"16\">₀</tspan>\n");
// Add the primes (′)
if (nprimes > 0) {
strcat(outbuffer, " <tspan dx=\"-6\" dy=\"-10\" font-size=\"16\">");
for (int i = 0; i < nprimes; ++i) {
strcat(outbuffer, "′"); // UTF-8 character for the prime symbol
}
strcat(outbuffer, "</tspan>\n");
}
// End of the SVG element
strcat(outbuffer,
" </text>\n"
"</svg>\n");
}
/*__________________________________________________________________________*/
static void sendParameters(PDOCDATA pDocData,char *outbuffer, int outbuffersize)
/* Send the parameter names as <svg> elements, to be used for labeling
buttons or inserting into a dialog.
*/
{
int n = pDocData->DocVarData.nparameters;
int byteswritten;
for(int k = 0;k<n;k++)
{ parameter p = pDocData->DocVarData.parameters[k];
int j = p.index;
term t = get_varlist()[j];
char id[16];
sprintf(id,"param%d",k);
if(pDocData->mainchoice == HDE && k > 1)
{ char *text = p.name;
// text will have the form "y''_0" for example.
char *marker = strchr(text,'\'');
int nprimes = 0;
if(!marker)
termtoSVGElementNoPadding(t,outbuffer, id, "param", outbuffersize);
else
{ while(marker && *marker == '\'')
{ ++marker;
++nprimes;
}
char y = text[0];
createPrimeParameter(y,nprimes,outbuffer,id);
}
}
else
{
termtoSVGElementNoPadding(t,outbuffer, id, "param", outbuffersize);
}
byteswritten = (int) strlen(outbuffer);
assert(byteswritten < outbuffersize);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
}
}
/*__________________________________________________________________________*/
int sendSymbolDocument(PDOCDATA pDocData, char *outbuffer, int outbuffersize)
// send a series of line, reason, comment, finalremark, and assumption SVG elements
{
unsigned long byteswritten;
void *savenode = heapmax();
// a lot of memory will be used by bblock and termtoSVG etc.
// so at the end we'll restore the heap to here.
// Otherwise, a series of 20 Undo commands will exhaust the heap.
proverdata q = pDocData->DocProverData;
vardata p = pDocData->DocVarData;
int betweenlines = 12;
int nlines = p.currentline+1; // +1 because they start at zero
int i;
char label[32];
char label2[32];
coord nexty = 0;
term t;
coord reasonstart = pDocData->papyrus->reasonstart;
// printf("in sendSymbolDocument, reasonstart is %d\n",reasonstart);
ltermlist *rr = (ltermlist *) pDocData->papyrus->selected;
int activeline = get_activeline(); // rr ? pDocData->papyrus->selectedline : 0;
rect r2;
r2.left = 0;
r2.right = pDocData->papyrus->right;
r2.top = 0;
r2.bottom = pDocData->papyrus->bottom;
set_svgDevice(outbuffer, outbuffersize,&r2);
svgDevice *theDevice = get_device();
/*
The language can be changed by Javascript within SymbolDoc.php,
and the change gets posted to pDocData->language, but after
each line, the browser window terminates, so we need to inform
SymbolDoc.php what the language is, because we want to avoid using
a session variable or a GET for that.
We need to send the language name in lower-case English, so we don't
use LanguageName, which would render it in the current language.
*/
char *languagename;
switch(pDocData->language) // which is a number
{ case ENGLISH: languagename = "english"; break;
case GERMAN: languagename = "german"; break;
case FRENCH: languagename = "french"; break;
case SPANISH: languagename = "spanish"; break;
case DUTCH: languagename = "dutch"; break;
case ITALIAN: languagename = "italian"; break;
case CHINESE: languagename = "chinese"; break;
default: assert(0);
}
/* we need to send Javascript to hide the selectTermHint as soon
as the user has taken a step. Originally I wanted to hide it
when they took a step by Term Selection. 1.21.25, I decided
to hide it also on autosteps. */
int autosteps = pDocData->DocControlData.totalautosteps;
int totalsteps = pDocData->DocControlData.totalsteps;
// printf("In sendSymbolDocument, autosteps = %d and totalsteps = %d\n", autosteps, totalsteps);
char *script;
if(totalsteps != 0) // originally it was autosteps > totalsteps
script = "<script>\n\t var hide_selectTermHint = true;\n</script>";
else
script = "<script>\n\t var hide_selectTermHint = false;\n</script>";
sprintf(outbuffer, "%s", script);
byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
int percent;
if(totalsteps == 0)
percent = 0;
else
percent = ((totalsteps-autosteps)*100 + 0.5)/totalsteps;
sprintf(outbuffer,
"<script>\n\t var documentID = %d;\n\t var languageName='%s';\n\t var percent = %d;\n\t var showGraphButton=%d;\n\t topic = %d\n\t var activeline=%d;\n</script>",
pDocData->docnumber,
languagename,
percent,
pDocData->showGraphButton,
pDocData->DocControlData.currenttopic,
activeline
);
// printf("%s", outbuffer); // DEBUG
byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
for(i=0;i<nlines;i++)
{
coord level,height,width;
char *reason;
sprintf(label,"line%d",i);
sprintf(label2,"reason%d",i);
t = q.history[i];
// MathXpert allows the user to select one equation and display only that one.
// We have to support that here.
if(FUNCTOR(t) == AND || FUNCTOR(t) == OR)
{ // check for an arg with the SELECTED bit set in its info field
for(int k = 0;k<ARITY(t);k++)
{ if(SELECTED(ARG(k,t)))
{ t = ARG(k,t);
break;
}
}
}
if(rr && i == activeline) // there is a selected term or terms in this line
// Then we have to color the rectangle of the selected term BEFORE
// writing the selected term on top of the colored rectangle.
{ theDevice->next = outbuffer;
relinquish_device();
// next line will write to 'next'. Previous line synced next and device->next
begin_svg("highlight","highlight0");
for (ltermlist *q = rr; q; q=q->next)
{ BIGRECT z = q->data.r;
z.right += 1; // esthetics
if(z.top <= 30 && activeline != 0)
// when the selected term has been set by ShowStepFocus,
// the coordinates are relative to the active line. But
// here we need coordinates in the <svg> container.
{ z.top += nexty+6;
z.bottom += nexty+8;
}
svg_filledrectwithborderandclass("selectedRectangle",
z.left, z.top , z.right, z.bottom ,
RGB(255,255,0),RGB(255,0,0));
// giving it the class "selectedRectangle" allows it to be
// resized, repositioned, or removed by Javascript, which
// will be required if the type size has been changed.
// The Engine doesn't know about type size changes and
// the resulting changes in position of terms.
}
end_svg();
set_svgDevice(outbuffer, outbuffersize,&r2);
byteswritten = (unsigned long) strlen(theDevice->next);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
}
termtoAbsoluteSVGElement(t, outbuffer, label, "line", outbuffersize,0,nexty,0,&level,&width,&height);
byteswritten = strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
assert(outbuffersize > 100);
reason = (char *) pDocData->DocControlData.linedatahistory[i].reason;
// printf("In sendSymbolDocument, reason is %s\n", reason);
int reasoncolor = get_reasoncolor();
if(reason[0] =='$' && reason[1] == '$')
{ // reasons can begin with $$, in which case, what's before the closing $$ must be parseable
char *q = strstr(reason+2,"$$");
int k = (int)(q-reason) + 2;
assert(reason[k-1] == '$' && reason[k-2] == '$');
char buffer[300];
strncpy(buffer, reason+2,k-4);
buffer[k-4] = '\0'; // make sure buffer is null-terminated
term t;
char *rest;
int err = bparse(get_parser_flags(),&t,buffer, &rest);
if(err)
assert(0);
int x,y,reasonheight,reasonlevel;
x = reasonstart;
y = nexty+level-16;
int width2;
char *startofreason = outbuffer;
termtoAbsoluteSVGElement(t,outbuffer, label2,"reason",outbuffersize,x,y,reasoncolor,&reasonlevel,&width2,&reasonheight);
byteswritten = strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
// the next term and reason are printed with level matching reasonlevel.
// So the part above that level is max(level, reasonlevel) and
// below is max(height-level, reasonheight-reasonlevel)
nexty += betweenlines;
if(height-level > reasonheight-reasonlevel)
nexty += height-level;
else
nexty += reasonheight-reasonlevel;
if(level > reasonlevel)
nexty += level;
else
nexty += reasonlevel;
if (k != strlen(reason))
{ // back up before "</svg>" and insert one more <text> element
// for the rest of the reason
int restwidth;
svgSymbolText(reason+k, outbuffer-7, outbuffersize+7, reasoncolor,
width2, reasonlevel-1, 12, &restwidth);
strcat(outbuffer,"</svg>\n");
byteswritten = strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
int totalwidth = width2 + restwidth*9; // restwidth is in characters
// this is the true width of the <svg> element
// change it in outbuffer
char *q = strstr(startofreason,"width");
q += 7; // so now it points to the numbers of width
char temp = q[3];
sprintf(q,"%03d",totalwidth); // That's why we always use 3 digits
q[3] = temp; // it was written over with a null character
// if width had MORE than 3 then the new width will be garbage,
// but in that case, the formula is too big for a reason anyway.
// Now we also have to change the viewbox width
q = strstr(startofreason, "viewBox");
// viewBox = " 0 0 is what is there
q+=16; // now it points to the numbers of width;
temp = q[5];
sprintf(q,"%05d",totalwidth);
q[5] = temp;
}
}
else
{
svgSymbolTextElement(reason,outbuffer,label2,"reason",outbuffersize,reasoncolor,
reasonstart, nexty + level); // 2.11.25 removed -8. This looks better
nexty += height + betweenlines;
byteswritten = strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
}
assert(outbuffersize > 100);
// Now, there might be a "permanent comment" associated with this line,
// and if so it is displayed after the line.
char *theComment = (char *) get_comment(pDocData, i);
if(theComment == NULL)
continue; // there was no comment for this line
// so there was a comment, send it
int color = get_textcolor();
char id[32];
sprintf(id,"comment_%d",i);
svgSymbolTextElement(theComment, outbuffer,id,"comment",outbuffersize,color,0,nexty);
int byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
nexty += 30; // comments better not contain tall displayed formulas
}
/* Now we've sent the lines and reasons, which are meant to be visible;
but we also need to send the assumptions, comments, progress elements, and final remark,
which will be displayed at the client's discretion.
*/
int nerrors = sendErrors(pDocData,outbuffer,outbuffersize,nexty);
// remove the errors from the papyrus after they're sent, so they
// don't persist after the next step.
nexty += nerrors * 16;
memset(pDocData->papyrus->error_buffer,0,DIM_ERROR_BUFFER *ERRBUFLENGTH);
byteswritten = strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
int ncomments = sendComments(pDocData,outbuffer,outbuffersize,nexty); // the temporary comments
// remove these comments from the papyrus after they're sent, so they
// don't persist after the next step.
nexty += ncomments*16;
memset(pDocData->papyrus->comment_buffer,0,DIM_COMMENT_BUFFER * COMMENTBUFLENGTH);
byteswritten = strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
char id[32];
sendAuthorsCommentary(pDocData,outbuffer,outbuffersize);
byteswritten = strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
sendAssumptions(pDocData,outbuffer,outbuffersize);
byteswritten = strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
sendToolTips(pDocData,outbuffer,outbuffersize);
byteswritten = strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
char *remark = (char *) pDocData->papyrus->finalremark;
if(remark[0])
{ sprintf(id, "finalremark");
int color = 0; // black by default
if(pDocData->DocControlData.finishedflag)
{ // The problem is done acceptably (even if automode might do more)
// so generate a "final remark"
svgSymbolTextElement(remark, outbuffer,id,"remark", outbuffersize,color,0,nexty);
byteswritten = strlen(outbuffer);
outbuffer+= byteswritten;
outbuffersize -= byteswritten;
// calculate the percent of the steps the user did
int autosteps = pDocData->DocControlData.totalautosteps;
int totalsteps = pDocData->DocControlData.totalsteps;
int percent = ((totalsteps-autosteps)*100 + 0.5)/totalsteps;
if(pDocData->DocControlData.solvedflag // the problem is solved
&& percent > 50 // and they did more than half the steps
&& totalsteps > 3 // don't applaud if they took 1 out of 2 steps
)
{ // issue the Javascript command to play cheering
char *script = "<script> playCheerSound(); </script>";
strcpy(outbuffer,script);
byteswritten = strlen(outbuffer);
outbuffer+= byteswritten;
outbuffersize -= byteswritten;
}
}
else
{
svgSymbolTextInvisibleElement(remark, outbuffer,id,"remark", outbuffersize,color,0,0);
byteswritten = strlen(outbuffer);
outbuffer+= byteswritten;
outbuffersize -= byteswritten;
}
printf("sent final remark \"%s\" as SVG\n", remark);
}
char *hint =(char *) pDocData->papyrus->hint;
if(hint[0])
{ sprintf(id, "hint0");
int color = 0; // black by default
svgSymbolTextInvisibleElement(hint, outbuffer,id,"hint", outbuffersize,color,0,0);
byteswritten = strlen(outbuffer);
outbuffer+= byteswritten;
outbuffersize -= byteswritten;
printf("sent hint \"%s\" as SVG\n", hint);
}
term *progress = pDocData->progress;
if(progress)
{ // there are progress elements to send;
sendProgressElements(pDocData,outbuffer,outbuffersize);
byteswritten = strlen(outbuffer);
outbuffer += byteswritten;
outbuffersize -= byteswritten;
}
reset_heap(savenode);
return 0;
}
/*____________________________________________________________________*/
void ndcToPixel(double s, double t, double *px, double *py, graph *g)
/* (s,t) are normalized device coordinates, running from 0 to 1 with (0,0)
in the lower left of the rectangle R containing all the graphs but not the Graph Toolbar.
(px,py) are pixel coordinates in rectangle R.
g->pymin and g->pymax are also pixel coordinates in R.
*/
{
double graphDeviceWidth = g->dxmax-g->dxmin;
double graphDeviceHeight = g->dymax-g->dymin;
double graphPixelWidth = g->pxmax-g->pxmin;
double graphPixelHeight = g->pymax-g->pymin;
double xpixelsPerDeviceUnit = graphPixelWidth/graphDeviceWidth;
double ypixelsPerDeviceUnit = graphPixelHeight/graphDeviceHeight;
*px = g->pxmin + (s-g->dxmin)*xpixelsPerDeviceUnit;
*py = g->pymax - (t-g->dymin)*ypixelsPerDeviceUnit;
}
/*____________________________________________________________________*/
static void sendJavaScriptGraph(PDOCDATA pDocData, char *outbuffer, int outbuffersize)
/* send code to define Javascript variables.
*/
{
int byteswritten,i;
/* Sent the language name and document ID */
char languageName[32];
strcpy(languageName,LanguageName(pDocData->language));
languageName[0] = tolower(languageName[0]); // change English to english
sprintf(outbuffer, "<script>\n\t var documentID = %d;\n\t var languageName='%s';\n\t var activeParameter=%d;\n</script>",
pDocData->docnumber,languageName,pDocData->DocVarData.activeparameter);
byteswritten = (int) strlen(outbuffer);
outbuffer += byteswritten;
/* Send the graph type and graph paper index */
sprintf(outbuffer, "\n <script>\n let graphtypeNumber = %d;\n let graphPaperIndex = %d;\n </script>\n",
pDocData->mainchoice,
pDocData->graphs[0]->grpaper.index
);
byteswritten = (int) strlen(outbuffer);
outbuffer+= byteswritten;
outbuffersize -= byteswritten;
/* Next send the array of parameters. Each parameter
has a name, index, value, and increment. The parameters
are stored in pDocData->DocVarData, and have to end up
in Javascript in an array of Parameter, where Parameter is
a Javascript class. We'll use JSON for this. */
char jsonString[2048];
char paramString[256]; // Buffer for each parameter.
// Start the JSON string.
sprintf(jsonString, "<script>\n\tvar jsonParameterData = {\n\t\t\"parameters\": [\n");
int nparameters = pDocData->DocVarData.nparameters;
parameter *parameters = pDocData->DocVarData.parameters;
// Add each parameter.
for (i = 0; i < nparameters; i++)
{
sprintf(paramString, "\t\t\t{\"value\": %f,\"name\": \"%s\", \"increment\": %f, \"index\": %d, \"rangemin\": %f, \"rangemax\": %f}%s\n",
*(parameters[i].addr),
parameters[i].name,
parameters[i].increment,
i, // index in the parameter array
parameters[i].rangemin,
parameters[i].rangemax,
// Add a comma if it's not the last parameter.
(i < nparameters - 1) ? "," : "");
// Concatenate the parameter to the JSON string.
strcat(jsonString, paramString);
}
// Close the JSON array and object.
strcat(jsonString, "\t\t]\n\t};\n</script>\n");
sprintf(outbuffer,"%s",jsonString);
byteswritten = (int)strlen(outbuffer);
outbuffer+= byteswritten;
outbuffersize-= byteswritten;
// Now send the variable eraseOld, which if nonzero means to
// erase all the old graphs made with different values of the parameters
int eraseOld = pDocData->graphs[0]->erase;
sprintf(outbuffer, "\n <script>\n let eraseOld = %d;\n</script>\n",eraseOld);
byteswritten = (int)strlen(outbuffer);
outbuffer+= byteswritten;
outbuffersize-= byteswritten;
}
/*___________________________________________________________________*/
void sendTitle(graph *g, int i, parameter *p)
/* send the title g->title as SVG to theDevice
where g = pDocData->graphs[i] and *p = is the active parameter if
there is one and g->function contains it, else NULL.
*/
{ svgDevice * theDevice = get_device();
term bt = g->title;
char id[32];
int byteswritten;
destroy_bblocked_term(bt);
int save_anddisplay = get_anddisplay();
set_anddisplay(1); // no brackets, typeset as matrix with no bars
make_title(g,&g->title); // in case a parameter value changed
set_anddisplay(save_anddisplay);
term title = g->title; // this term is already bblocked!
// where should the title go? (g->txmin, g->txmax) are normalized device coordinates
double s = g->txmin;
double t = g->tymax; // not ymin, since normalized device coordinates increase upwards.
double x,y;
ndcToPixel(s,t,&x,&y,g);
int level, width,height;
if(p != NULL)
{ // don't send 100 elements with the same id
double pval = *(p->addr);
if (fabs(pval) < 1e-10) // Tiny threshold
pval = 0.0; // prevent outputting "-0.0000"
sprintf(id,"title%d-%.4f",i,pval);
blockedTermtoAbsoluteSVGElement2(title, theDevice->next, id, "title", theDevice->bytesavailable,x,y,g->titlecolor,&level,&width,&height,p);
// that, in effect, sends it to PHP, tagged with "param"
}
else
{ sprintf(id,"title%d",i);
blockedTermtoAbsoluteSVGElement(title, theDevice->next, id, "title", theDevice->bytesavailable,x,y,g->titlecolor,&level,&width,&height);
// that, in effect, sends it to PHP
}
byteswritten = (int) strlen(theDevice->next);
theDevice->bytesavailable -= byteswritten;
theDevice->next += byteswritten;
relinquish_device();
}
/*____________________________________________________________________*/
int sendGraphDocument(PDOCDATA pDocData)
/* Write the HTML and SVG required to display the document to theDevice->next
If the amount of data exceeds outbuffersize, an assertion failure will
result, so the allocated space had better be sufficient. This
function neither allocates nor destroys outbuffer.
The response will just be echoed to the browser by PHP
It includes <svg> elements for the graph lines, with absolute
positioning. (Therefore the client needs to place them inside a <div> with
relative or absolute positioning.) When there are several graphs, the
Engine lays them out; the interface has only to display them.
The response does not include the user controls called (in Windows MathXpert)
the Graph Toolbar. It does, however, include <svg> elements with class
toolbartext and ids as needed for the toolbars and class
toolbarhovertext for pop-up explanatory text, and class
title, with ids title0, title1,...title6, for the titles.
*/
{
int ngraphs = pDocData->ngraphs;
int i,err,byteswritten;
svgDevice * theDevice = get_device();
sendJavaScriptGraph(pDocData, theDevice->next, theDevice->bytesavailable);
byteswritten = (int) strlen(theDevice->next);
theDevice->next += byteswritten;
relinquish_device();
if(pDocData->graphs[0]->graphtype == MC_INEQ)
ngraphs = 1; // there are two graph structures, but only one graph is sent to the browser;
// the second one is used in drawing the first one.
for(i=0;i<ngraphs;i++)
{ graph *g = pDocData->graphs[i];
g->activeparameter = pDocData->DocVarData.activeparameter; // index in parameters array
g->parameters = pDocData->DocVarData.parameters; // the parameter array itself
g->nparameters = pDocData->DocVarData.nparameters;
assert(g->activeparameter < g->nparameters); // OK even when nparameters is 0 as then activeparameter should be -1
char id[32];
rect r;
r.left = g->pxmin; // pixel coordinates of the viewport
r.right = g->pxmax;
r.top = g->pymin;
r.bottom = g->pymax;
sprintf(id,"graph%d",i);
int width2 = r.right-r.left;
int height2 = r.bottom-r.top;
// The <svg> must carry the viewport rectangle; it already has it
// as a ClipRect, but JavaScript can't extract it via the DOM model, so we
// tuck it into the <svg> declaration.
svgDevice * theDevice = get_device();
begin_svg_withmyrect("graph",id,r.top,r.left,width2, height2);
if(g->border != g->background)
{ // then outline the viewport. We don't want it to scroll
svg_rect(r.left, r.top, r.right,r.bottom, g->border);
r.left+=2;
r.top+=2;
r.bottom-=2;
r.right-=2;
}
set_cliprect(r,i);
char groupId[32];
sprintf(groupId,"scrollable%d",i);
// Next emit "<g id="scrollable0"> (when i is 0)
begin_svgGroup("scrollable", groupId);
if(pDocData->graphs[0]->graphtype == MC_INEQ)
{ err = draw_inequality2(pDocData);
if(err)
err = 1061;
/* "MathXpert cannot draw that graph, sorry. Please try something else." */
}
else
err = drawLarge(g); // replacing draw(g) to support scrolling
end_svgGroup();
end_svg();
if(err == 11) // out of space
{ assert(0); // then crash recovery will take over
}
if(err)
{
/* Below are the possible warnings, copied from draw.c.
The graph will still be drawn, except in case 11, so
these are really warnings, not errors. */
int errNumber = 0;
switch(err)
{ case 1:
errNumber = 1246;
/* MathXpert could not compute the singularities. */
break;
case 2:
errNumber = 1247;
/* Function nowhere defined on the interval shown.*/
break;
case 3:
errNumber = 1254;
/* MathXpert could not compute the roots. */
/* (of a polynomial in POLYROOTS) */
break;
case 4:
errNumber = 1255;
/* MathXpert could not compute the solution. */
/* (of a differential equation) */
/* We don't send an error message for this, see below,
because the solutions look fine. It's more than 10,000 steps
in Runge-Kutta. But 10,000 steps makes a nice solution.
*/
break;
case 6: // (there is no case 5)
errNumber = 1375;
/* MathXpert could not compute the jumps. */
break;
case 7:
errNumber = 1376;
/* MathXpert could compute neither the
singularities nor the jumps. */
break;
case 9: // (there is no case 8)
errNumber = 1246;
/* MathXpert could not compute the singularities. */
/* actually, it could not compute the numerical
singularities, but could compute the symbolic ones.
*/
break;
case 10:
errNumber = 1248;
/* No graph visible */
/* actually:
function defined only at isolated points in the interval, no graph visible. This can be due to
too many BADVAL values, as in graphing 1/x^1000000000.
*/
break;
case 1061:
errNumber = 1061;
/* "MathXpert cannot draw that graph, sorry. Please try something else." */
default:
printf("%s", "unexpected return value from draw, oops!"); // those are all the possible errors
}
if(errNumber > 0 && errNumber != 1255)
/* The exception for 1255 is explained a few lines above. */
{ char *warning = (char *) english(errNumber);
// send the warning to PHP as SVG text
char id[32];
sprintf(id,"warning%d",errNumber);
begin_svg("warning",id);
theDevice = get_device();
svgwriteText(warning, theDevice->next, RGB(255,0,0), ROMAN);
byteswritten = (int)strlen(theDevice->next);
theDevice->bytesavailable -= byteswritten;
theDevice->next += byteswritten;
relinquish_device();
end_svg();
}
}
sendTitle(g,i,NULL); // No 'else'. This ensures there is a title with id title0 (etc.)
// titles labeled with "param" are send in sendPaths
}
theDevice = get_device();
sendAssumptions(pDocData,theDevice->next,theDevice->bytesavailable);
byteswritten = (int) strlen(theDevice->next);
theDevice->next += byteswritten;
theDevice->bytesavailable -= byteswritten;
sendSingularities(pDocData, theDevice->next,theDevice->bytesavailable);
byteswritten = (int) strlen(theDevice->next);
theDevice->next += byteswritten;
theDevice->bytesavailable -= byteswritten;
sendParameters(pDocData,theDevice->next,theDevice->bytesavailable);
byteswritten = (int) strlen(theDevice->next);
theDevice->next += byteswritten;
theDevice->bytesavailable -= byteswritten;
sendJumps(pDocData, theDevice->next,theDevice->bytesavailable);
byteswritten = (int) strlen(theDevice->next);
theDevice->next += byteswritten;
theDevice->bytesavailable -= byteswritten;
sendAuthorsCommentary(pDocData,theDevice->next,theDevice->bytesavailable);
byteswritten = (int) strlen(theDevice->next);
theDevice->next += byteswritten;
theDevice->bytesavailable -= byteswritten;
sendToolTips(pDocData, theDevice->next, theDevice->bytesavailable);
byteswritten = (int) strlen(theDevice->next);
theDevice->next += byteswritten;
theDevice->bytesavailable -= byteswritten;
relinquish_device();
return 0;
}
#if 0
/*___________________________________________________*/
#include "operator.h"
// This code is used to test svgSymbolText. To use it,
// temporarily rename main to something else.
int main(void)
/* check if svgSymbolText can handle all the natural-language strings
*/
{ int k;
char svgOut[10000];
char id[32];
char id2[32];
char id3[32];
int testingEnglish = 1; // or set it to 1
int testingTooltips = 0; // or set it to 0, but only test one of the two
int y = 0;
int deltay = testingEnglish ? 20 : 60;
int languageNumber = ENGLISH;
PDOCDATA pDocData = (PDOCDATA) calloc(1,sizeof(DOCDATA));
pDocData->language = languageNumber;
init_doc_data(SYMBOLDOC, pDocData); // includes create_heap
activate(pDocData);
FILE *fp = fopen("/Users/beeson/Dropbox/MathXpert/tests/EnglishTest.svg","w+");
fprintf(fp,"<div>\n");
int nEnglish = 2519;
int nOpHelp = 16* (int)functions_menu;
/* functions_menu is one past the last menu containing strings */
/* There are 16 items per menu, but some may be blank. */
char *test, *menutext, *hinttext;
int nItems = testingEnglish ? nEnglish : nOpHelp;
for(k=0;k<nItems;k++, y+=deltay)
{
if(testingEnglish)
{
printf("%d\n",k);
sprintf(id,"english%d",k);
test = (char *) english(k);
}
else if(testingTooltips)
{
printf("%d %d %d\n",k, k/16, k%16);
int menu = k/16;
int op = k % 16;
if(op == 0)
y += 16;
sprintf(id,"ophelp%d-%d",menu,op);
test = (char *) ophelp(menu)[op];
menutext = (char *) cmdmenu(menu)[op];
sprintf(id2,"menutext%d-%d",menu,op);
hinttext = (char *) hints(menu,op);
sprintf(id3,"hinttext%d-%d",menu,op);
}
else
{ assert(0);
}
if(test==NULL || !strcmp(test,""))
{ y -= deltay;
continue; // nothing to print
}
if( strstr(test,"$$"))
{ svgSymbolTextElement(test, svgOut,id, "test", 10000,RGB(255,0,0),0,y);
fprintf(fp,"%s",svgOut);
y+=30;
}
else
{
svgSymbolTextElement(test, svgOut,id, "test", 10000,0,0,y);
fprintf(fp,"%s",svgOut);
}
if(testingTooltips) // also print the menu text
{ int color = RGB(128,255,255);
if(strstr(menutext,"$$"))
{ color = RGB(0,0,255);
}
svgSymbolTextElement(menutext, svgOut, id2, "menuitem", 10000, RGB(0,128,128), 0, y+20);
if(strstr(menutext,"$$"))
y += 30;
fprintf(fp,"%s",svgOut);
// and also the hint text
svgSymbolTextElement(hinttext, svgOut, id2, "hint", 10000, RGB(0,0,255), 0, y+40);
y+= 30;
fprintf(fp,"%s",svgOut);
}
}
fprintf(fp,"</div>\n");
fclose(fp);
return 1;
}
#endif
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists