diff --git a/.gitattributes b/.gitattributes index efe3262f890dfd7a9d48dc45578a711a65391ae0..b1f4400dce038d50e82bb093784207d24c657685 100644 --- a/.gitattributes +++ b/.gitattributes @@ -39,6 +39,11 @@ testfont binary /lib/modules/Debug.pmod/werror.pmod foreign_ident /lib/modules/Filesystem.pmod/Tar.pmod foreign_ident /lib/modules/GLU.pmod foreign_ident +/lib/modules/Graphics.pmod/Graph.pmod/create_bars.pike foreign_ident +/lib/modules/Graphics.pmod/Graph.pmod/create_graph.pike foreign_ident +/lib/modules/Graphics.pmod/Graph.pmod/create_pie.pike foreign_ident +/lib/modules/Graphics.pmod/Graph.pmod/graph.h foreign_ident +/lib/modules/Graphics.pmod/Graph.pmod/polyline.pike foreign_ident /lib/modules/LR.pmod/Grammar_parser.pmod foreign_ident /lib/modules/LR.pmod/item.pike foreign_ident /lib/modules/LR.pmod/lr.pike foreign_ident diff --git a/lib/modules/Graphics.pmod/Graph.pmod/create_bars.pike b/lib/modules/Graphics.pmod/Graph.pmod/create_bars.pike new file mode 100755 index 0000000000000000000000000000000000000000..b143252dba56c3301d5eccb51cb197c8402165c3 --- /dev/null +++ b/lib/modules/Graphics.pmod/Graph.pmod/create_bars.pike @@ -0,0 +1,821 @@ +#!NOMODULE + +#include "graph.h" + +import Image; +import Array; +import Stdio; + +inherit "polyline.pike"; +inherit "create_graph.pike"; + +constant cvs_version = "$Id: create_bars.pike,v 1.1 1999/09/30 13:03:56 hedda Exp $"; + +/* + * name = "BG: Create bars"; + * doc = "Business Graphics sub-module for drawing bars."; + */ + +/* +These functions were written by Henrik "Hedda" Wallin (hedda@idonex.se) +Create_bars can draw normal bars, sumbars and normalized sumbars. +*/ + +mapping(string:mixed) create_bars(mapping(string:mixed) diagram_data) +{ + +#ifdef BG_DEBUG + mapping bg_timers = ([]); +#endif + + //Supports only xsize>=100 + + int si=diagram_data["fontsize"]; + + //Fix defaultcolors! + setinitcolors(diagram_data); + + + string where_is_ax; + + object(image) barsdiagram; + +#ifdef BG_DEBUG + bg_timers->init_bg = gauge { +#endif + init_bg(diagram_data); +#ifdef BG_DEBUG + }; +#endif + barsdiagram=diagram_data["image"]; + +#ifdef BG_DEBUG + bg_timers->set_legend_size = gauge { +#endif + set_legend_size(diagram_data); +#ifdef BG_DEBUG + }; +#endif + //write("ysize:"+diagram_data["ysize"]+"\n"); + diagram_data["ysize"]-=diagram_data["legend_size"]; + //write("ysize:"+diagram_data["ysize"]+"\n"); + +#ifdef BG_DEBUG + bg_timers->init = gauge { +#endif + + //Calculate biggest and smallest datavalues + init(diagram_data); +#ifdef BG_DEBUG + }; +#endif + + //Calculate how many and how big the texts are. +#ifdef BG_DEBUG + bg_timers->space = gauge { +#endif + if (!(diagram_data["xspace"])) + { + //Initiate the distance between the texts on the x-axis (Or X-axis?) + float range=(diagram_data["xmaxvalue"]- + diagram_data["xminvalue"]); + float space=pow(10.0, floor(log(range/3.0)/log(10.0))); + if (range/space>5.0) + { + if(range/(2.0*space)>5.0) + { + space=space*5.0; + } + else + space=space*2.0; + } + else + if (range/space<2.5) + space*=0.5; + diagram_data["xspace"]=space; + } + if (!(diagram_data["yspace"])) + { + //Initiate the distance between the texts on the y-axis (Or X-axis?) + float range=(diagram_data["ymaxvalue"]- + diagram_data["yminvalue"]); + float space=pow(10.0, floor(log(range/3.0)/log(10.0))); + if (range/space>5.0) + { + if(range/(2.0*space)>5.0) + space *= 5.0; + else + space *= 2.0; + } + else + if (range/space<2.5) + space *= 0.5; + diagram_data["yspace"]=space; + } + +#ifdef BG_DEBUG + }; +#endif + + +#ifdef BG_DEBUG + bg_timers->text = gauge { +#endif + + + float start; + start=diagram_data["xminvalue"]+diagram_data["xspace"]/2.0; + diagram_data["values_for_xnames"]=allocate(sizeof(diagram_data["xnames"])); + for(int i=0; i<sizeof(diagram_data["xnames"]); i++) + diagram_data["values_for_xnames"][i]=start+start*2*i; + + if (!(diagram_data["values_for_ynames"])) + { + if ((diagram_data["yspace"]<LITET) + && (diagram_data["yspace"]>-LITET)) + throw( ({"Very bad error because yspace is zero!\n", + backtrace()})); + float start; + start=diagram_data["yminvalue"]; + start=diagram_data["yspace"]*ceil((start)/diagram_data["yspace"]); + diagram_data["values_for_ynames"]=({start}); + while(diagram_data["values_for_ynames"][-1]<= + diagram_data["ymaxvalue"]-diagram_data["yspace"]) + diagram_data["values_for_ynames"]+=({start+=diagram_data["yspace"]}); + } + + function fun; + if (diagram_data["eng"]) + fun=diagram_eng; + else + fun=diagram_neng; + + //Draw the text if it does not exist + if (!(diagram_data["ynames"])) + if (diagram_data["eng"]||diagram_data["neng"]) + { + diagram_data["ynames"]= + allocate(sizeof(diagram_data["values_for_ynames"])); + array(mixed) v=diagram_data["values_for_ynames"]; + mixed m=diagram_data["ymaxvalue"]; + mixed mi=diagram_data["yminvalue"]; + for(int i=0; i<sizeof(v); i++) + if (abs(v[i]*1000)<max(m, abs(mi))) + diagram_data["ynames"][i]="0"; + else + diagram_data["ynames"][i]= + fun((float)(v[i])); + } + else + { + diagram_data["ynames"]= + allocate(sizeof(diagram_data["values_for_ynames"])); + + for(int i=0; i<sizeof(diagram_data["values_for_ynames"]); i++) + diagram_data["ynames"][i]= + no_end_zeros((string)(diagram_data["values_for_ynames"][i])); + } + + + if (!(diagram_data["xnames"])) + { + diagram_data["xnames"]= + allocate(sizeof(diagram_data["values_for_xnames"])); + + for(int i=0; i<sizeof(diagram_data["values_for_xnames"]); i++) + diagram_data["xnames"][i]= + no_end_zeros((string)(diagram_data["values_for_xnames"][i])); + } + + + //Draw the text-images + //calculate xmaxynames, ymaxynames xmaxxnames ymaxxnames + create_text(diagram_data); + si=diagram_data["fontsize"]; + +#ifdef BG_DEBUG + }; +#endif + + //Create the label-texts for the X-axis + object labelimg; + string label; + int labelx=0; + int labely=0; + if (diagram_data["labels"]) + { + if (diagram_data["labels"][2] && sizeof(diagram_data["labels"][2])) + label=diagram_data["labels"][0]+" ["+diagram_data["labels"][2]+"]"; //Xstorhet + else + label=diagram_data["labels"][0]; + + GETFONT(xaxisfont); + if ((label!="")&&(label!=0)) + labelimg=notext + ->write(UNICODE(label,diagram_data["encoding"])) + ->scale(0,diagram_data["labelsize"]); + else + labelimg=image(diagram_data["labelsize"],diagram_data["labelsize"]); + + if (labelimg->xsize()<1) + labelimg=image(diagram_data["labelsize"],diagram_data["labelsize"]); + + if (labelimg->xsize()> + diagram_data["xsize"]/2) + labelimg=labelimg->scale(diagram_data["xsize"]/2, 0); + + labely=diagram_data["labelsize"]; + labelx=labelimg->xsize(); + } + else + diagram_data["labelsize"]=0; + + labely+=write_name(diagram_data); + + int ypos_for_xaxis; //Distance from bottom + int xpos_for_yaxis; //Distance from right + + //Decide where the graph can be drawn + diagram_data["ystart"]=(int)ceil(diagram_data["linewidth"]); + diagram_data["ystop"]=diagram_data["ysize"]- + (int)ceil(diagram_data["linewidth"]+si)-labely; + if (((float)diagram_data["yminvalue"]>-LITET)&& + ((float)diagram_data["yminvalue"]<LITET)) + diagram_data["yminvalue"]=0.0; + + if (diagram_data["yminvalue"]<0) + { + //Calculate position for the x-axis. + //If this doesn't work: Draw the y-axis at right or left + // and recalculate diagram_data["ystart"] + ypos_for_xaxis=((-diagram_data["yminvalue"]) + * (diagram_data["ystop"] - diagram_data["ystart"])) + / (diagram_data["ymaxvalue"]-diagram_data["yminvalue"]) + + diagram_data["ystart"]; + + int minpos; + minpos=max(labely, diagram_data["ymaxxnames"])+si/2; + if (minpos>ypos_for_xaxis) + { + ypos_for_xaxis=minpos; + diagram_data["ystart"]=ypos_for_xaxis+ + diagram_data["yminvalue"]*(diagram_data["ystop"]-ypos_for_xaxis)/ + (diagram_data["ymaxvalue"]); + } + else + { + int maxpos; + maxpos=diagram_data["ysize"]- + (int)ceil(diagram_data["linewidth"]+si*2) + - labely; + if (maxpos<ypos_for_xaxis) + { + ypos_for_xaxis=maxpos; + diagram_data["ystop"]=ypos_for_xaxis + + diagram_data["ymaxvalue"]*(ypos_for_xaxis-diagram_data["ystart"]) + / (0-diagram_data["yminvalue"]); + } + } + } + else + if (diagram_data["yminvalue"]==0.0) + { + // Place the x-axis and diagram_data["ystart"] at bottom. + diagram_data["ystop"]=diagram_data["ysize"] + - (int)ceil(diagram_data["linewidth"]+si)-labely; + ypos_for_xaxis=max(labely, diagram_data["ymaxxnames"])+si/2; + diagram_data["ystart"]=ypos_for_xaxis; + } + else + { + //Place the x-axis at bottom and diagram_data["ystart"] a + //Little bit higher + diagram_data["ystop"]=diagram_data["ysize"] + - (int)ceil(diagram_data["linewidth"]+si)-labely; + ypos_for_xaxis=max(labely, diagram_data["ymaxxnames"])+si/2; + diagram_data["ystart"]=ypos_for_xaxis+si*2; + } + + //Calculate position for the y-axis + diagram_data["xstart"]=(int)ceil(diagram_data["linewidth"]); + diagram_data["xstop"]=diagram_data["xsize"]- + (int)ceil(diagram_data["linewidth"])-max(si,labelx+si/2)- + diagram_data["xmaxxnames"]/2; + if (((float)diagram_data["xminvalue"]>-LITET)&& + ((float)diagram_data["xminvalue"]<LITET)) + diagram_data["xminvalue"]=0.0; + + if (diagram_data["xminvalue"]<0) + { + //Calculate position for the y-axis. + //If this doesn't work: Draw the y-axis at right or left + // and recalculate diagram_data["xstart"] + xpos_for_yaxis=((-diagram_data["xminvalue"]) + * (diagram_data["xstop"]-diagram_data["xstart"])) + / (diagram_data["xmaxvalue"]-diagram_data["xminvalue"]) + + diagram_data["xstart"]; + + int minpos; + minpos=diagram_data["xmaxynames"]+si/2; + if (minpos>xpos_for_yaxis) + { + xpos_for_yaxis=minpos; + diagram_data["xstart"]=xpos_for_yaxis+ + diagram_data["xminvalue"]*(diagram_data["xstop"]-xpos_for_yaxis)/ + (diagram_data["ymaxvalue"]); + } + else + { + int maxpos; + maxpos=diagram_data["xsize"]- + (int)ceil((float)diagram_data["linewidth"]+si*2+labelx); + if (maxpos<xpos_for_yaxis) + { + xpos_for_yaxis=maxpos; + diagram_data["xstop"]=xpos_for_yaxis+ + diagram_data["xmaxvalue"]*(xpos_for_yaxis-diagram_data["xstart"])/ + (0-diagram_data["xminvalue"]); + } + } + } + else + if (diagram_data["xminvalue"]==0.0) + { + //Place the y-axis at left (?) and diagram_data["xstart"] at + // the same place + diagram_data["xstop"]=diagram_data["xsize"]- + (int)ceil(diagram_data["linewidth"])-max(si,labelx+si/2)- + diagram_data["xmaxxnames"]/2; + xpos_for_yaxis=diagram_data["xmaxynames"]+si/2+2; + diagram_data["xstart"]=xpos_for_yaxis+si/2; + } + else + { + //Place the y-axis at left (?) and diagram_data["xstart"] a + // little bit more right (?) + diagram_data["xstop"]=diagram_data["xsize"]- + (int)ceil(diagram_data["linewidth"])-max(si,labelx+si/2)- + diagram_data["xmaxxnames"]/2; + xpos_for_yaxis=diagram_data["xmaxynames"]+si/2; + diagram_data["xstart"]=xpos_for_yaxis+si*2; + } + + //Calculate some shit + float xstart=(float)diagram_data["xstart"]; + float xmore=(-xstart+diagram_data["xstop"])/ + (diagram_data["xmaxvalue"]-diagram_data["xminvalue"]); + float ystart=(float)diagram_data["ystart"]; + float ymore=(-ystart+diagram_data["ystop"])/ + (diagram_data["ymaxvalue"]-diagram_data["yminvalue"]); + +#ifdef BG_DEBUG + bg_timers->draw_grid = gauge { +#endif + + draw_grid(diagram_data, xpos_for_yaxis, ypos_for_xaxis, + xmore, ymore, xstart, ystart, (float) si); + +#ifdef BG_DEBUG + }; +#endif + + + //??? + int farg=0; + + +#ifdef BG_DEBUG + bg_timers->draw_values = gauge { +#endif + + if (diagram_data["type"]=="sumbars") + { + int s=diagram_data["datasize"]; + float barw=diagram_data["xspace"]*xmore/3.0; + for(int i=0; i<s; i++) + { + int j=0; + float x,y; + x=xstart+(diagram_data["xspace"]/2.0+diagram_data["xspace"]*i)* + xmore; + + y=-(-diagram_data["yminvalue"])*ymore+ + diagram_data["ysize"]-ystart; + float start=y; + + foreach(column(diagram_data["data"], i), float|string d) + { + if (d==VOIDSYMBOL) + d=0.0; + y-=d*ymore; + + barsdiagram->setcolor(@(diagram_data["datacolors"][j++])); + + barsdiagram->polygone( + ({x-barw, y + , x+barw, y, + x+barw, start + , x-barw, start + })); + /* barsdiagram->setcolor(0,0,0); + draw(barsdiagram, 0.5, + ({ + x-barw, start, + x-barw, y + , x+barw, y, + x+barw, start + + }) + ); + */ + start=y; + } + } + } + else + if (diagram_data["subtype"]=="line") + if (diagram_data["drawtype"]=="linear") + foreach(diagram_data["data"], array(float|string) d) + { + array(float|string) l=allocate(sizeof(d)*2); + for(int i=0; i<sizeof(d); i++) + if (d[i]==VOIDSYMBOL) + { + l[i*2]=VOIDSYMBOL; + l[i*2+1]=VOIDSYMBOL; + } + else + { + l[i*2]=xstart+(diagram_data["xspace"]/2.0+ + diagram_data["xspace"]*i) + * xmore; + l[i*2+1]=-(d[i]-diagram_data["yminvalue"])*ymore+ + diagram_data["ysize"]-ystart; + } + + //Draw Ugly outlines + if ((diagram_data["backdatacolors"])&& + (diagram_data["backlinewidth"])) + { + barsdiagram->setcolor(@(diagram_data["backdatacolors"][farg])); + draw(barsdiagram, diagram_data["backlinewidth"],l, + diagram_data["xspace"] ); + } + + barsdiagram->setcolor(@(diagram_data["datacolors"][farg++])); + draw(barsdiagram, diagram_data["graphlinewidth"],l); + } + else + throw( ({"\""+diagram_data["drawtype"] + + "\" is an unknown bars-diagram drawtype!\n", + backtrace()})); + else + if (diagram_data["subtype"]=="box") + if (diagram_data["drawtype"]=="2D") + { + int s=sizeof(diagram_data["data"]); + float barw=diagram_data["xspace"]*xmore/1.5; + float dnr=-barw/2.0+ barw/s/2.0; + barw/=s; + barw/=2.0; + farg=-1; + float yfoo=(float)(diagram_data["ysize"]-ypos_for_xaxis); + //"draw_values":3580, + foreach(diagram_data["data"], array(float|string) d) + { + farg++; + + for(int i=0; i<sizeof(d); i++) + if (d[i]!=VOIDSYMBOL) + { + float x,y; + x=xstart+(diagram_data["xspace"]/2.0+diagram_data["xspace"]*i)* + xmore; + y=-(d[i]-diagram_data["yminvalue"])*ymore+ + diagram_data["ysize"]-ystart; + barsdiagram->setcolor(@(diagram_data["datacolors"][farg])); + + barsdiagram->polygone( + ({x-barw+dnr, y + , x+barw+dnr, y, + x+barw+dnr, yfoo + , x-barw+dnr, yfoo + })); + /* barsdiagram->setcolor(0,0,0); + draw(barsdiagram, 0.5, + ({x-barw+dnr, y + , x+barw+dnr, y, + x+barw+dnr, diagram_data["ysize"]-ypos_for_xaxis + , x-barw+dnr,diagram_data["ysize"]- ypos_for_xaxis, + x-barw+dnr, y + }));*/ + } + dnr+=barw*2.0; + } + } + else + throw( ({"\""+diagram_data["drawtype"] + + "\" is an unknown bars-diagram drawtype!\n", backtrace()})); + else + throw( ({"\""+diagram_data["subtype"] + +"\" is an unknown bars-diagram subtype!\n", backtrace()})); + +#ifdef BG_DEBUG + }; +#endif + + //Draw X and Y-axis + barsdiagram->setcolor(@(diagram_data["axcolor"])); + + + //Draw the X-axis + if ((diagram_data["xminvalue"]<=LITET)&& + (diagram_data["xmaxvalue"]>=-LITET)) + barsdiagram-> + polygone(make_polygon_from_line(diagram_data["linewidth"], + ({ + xpos_for_yaxis, + diagram_data["ysize"]- ypos_for_xaxis, + diagram_data["xsize"]- + diagram_data["linewidth"]-labelx/2, + diagram_data["ysize"]-ypos_for_xaxis + }), + 1, 1)[0]); + else + if (diagram_data["xmaxvalue"]<-LITET) + { + barsdiagram + ->polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + xpos_for_yaxis, + diagram_data["ysize"]-ypos_for_xaxis, + + xpos_for_yaxis-4.0/3.0*si, + diagram_data["ysize"]-ypos_for_xaxis, + + xpos_for_yaxis-si, + diagram_data["ysize"]-ypos_for_xaxis-si/2.0, + xpos_for_yaxis-si/1.5, + diagram_data["ysize"]-ypos_for_xaxis+si/2.0, + + xpos_for_yaxis-si/3.0, + diagram_data["ysize"]-ypos_for_xaxis, + + diagram_data["xsize"]-diagram_data["linewidth"] + - labelx/2, + diagram_data["ysize"]-ypos_for_xaxis + }), 1, 1)[0]); + } + else + if (diagram_data["xminvalue"]>LITET) + { + barsdiagram + ->polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + xpos_for_yaxis, + diagram_data["ysize"]- ypos_for_xaxis, + + xpos_for_yaxis+si/3.0, + diagram_data["ysize"]-ypos_for_xaxis, + + xpos_for_yaxis+si/1.5, + diagram_data["ysize"]-ypos_for_xaxis-si/2.0, + xpos_for_yaxis+si, + diagram_data["ysize"]-ypos_for_xaxis+si/2.0, + + xpos_for_yaxis+4.0/3.0*si, + diagram_data["ysize"]-ypos_for_xaxis, + + diagram_data["xsize"]-diagram_data["linewidth"] + - labelx/2, + diagram_data["ysize"]-ypos_for_xaxis + }), 1, 1)[0]); + } + + //Draw arrow on the X-axis + if (diagram_data["subtype"]=="line") + barsdiagram->polygone( ({ + diagram_data["xsize"]-diagram_data["linewidth"]/2-(float)si-labelx/2, + diagram_data["ysize"]-ypos_for_xaxis-(float)si/4.0, + + diagram_data["xsize"]-diagram_data["linewidth"]/2-labelx/2, + diagram_data["ysize"]-ypos_for_xaxis, + + diagram_data["xsize"]-diagram_data["linewidth"]/2-(float)si-labelx/2, + diagram_data["ysize"]-ypos_for_xaxis+(float)si/4.0 + }) + ); + + //Draw Y-axis + if ((diagram_data["yminvalue"]<=LITET)&& + (diagram_data["ymaxvalue"]>=-LITET)) + { + if ((diagram_data["yminvalue"]<=LITET)&& + (diagram_data["yminvalue"]>=-LITET)) + barsdiagram-> + polygone(make_polygon_from_line(diagram_data["linewidth"], + ({ + xpos_for_yaxis, + diagram_data["ysize"]-ypos_for_xaxis, + xpos_for_yaxis, + si+labely + }), 1, 1)[0]); + else + barsdiagram-> + polygone(make_polygon_from_line(diagram_data["linewidth"], + ({ + xpos_for_yaxis, + diagram_data["ysize"] + - diagram_data["linewidth"], + + xpos_for_yaxis, + si+labely + }), 1, 1)[0]); + + } + else + if (diagram_data["ymaxvalue"]<-LITET) + { + barsdiagram-> + polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + xpos_for_yaxis, + diagram_data["ysize"]-diagram_data["linewidth"], + + xpos_for_yaxis, + diagram_data["ysize"]-ypos_for_xaxis+si*4.0/3.0, + + xpos_for_yaxis-si/2.0, + diagram_data["ysize"]-ypos_for_xaxis+si, + + xpos_for_yaxis+si/2.0, + diagram_data["ysize"]-ypos_for_xaxis+si/1.5, + + xpos_for_yaxis, + diagram_data["ysize"]-ypos_for_xaxis+si/3.0, + + xpos_for_yaxis, + si+labely + }), 1, 1)[0]); + } + else + if (diagram_data["yminvalue"]>LITET) + { + barsdiagram-> + polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + xpos_for_yaxis, + diagram_data["ysize"]-diagram_data["linewidth"], + + xpos_for_yaxis, + diagram_data["ysize"]-ypos_for_xaxis-si/3.0, + + xpos_for_yaxis-si/2.0, + diagram_data["ysize"]-ypos_for_xaxis-si/1.5, + + xpos_for_yaxis+si/2.0, + diagram_data["ysize"]-ypos_for_xaxis-si, + + xpos_for_yaxis, + diagram_data["ysize"]-ypos_for_xaxis-si*4.0/3.0, + + xpos_for_yaxis, + si+labely + }), 1, 1)[0]); + } + + //Draw arrow + barsdiagram-> + polygone( ({ + xpos_for_yaxis-(float)si/4.0, + diagram_data["linewidth"]/2.0+(float)si+labely, + + xpos_for_yaxis, + diagram_data["linewidth"]/2.0+labely, + + xpos_for_yaxis+(float)si/4.0, + diagram_data["linewidth"]/2.0+(float)si+labely + }) ); + + //Write the text on the X-axis + int s=sizeof(diagram_data["xnamesimg"]); + + + +#ifdef BG_DEBUG + bg_timers->text_on_axis = gauge { +#endif + + for(int i=0; i<s; i++) + if ((diagram_data["values_for_xnames"][i]<diagram_data["xmaxvalue"])&& + (diagram_data["values_for_xnames"][i]>diagram_data["xminvalue"])) + { + barsdiagram->paste_alpha_color( + diagram_data["xnamesimg"][i], + @(diagram_data["textcolor"]), + (int)floor((diagram_data["values_for_xnames"][i] + - diagram_data["xminvalue"])*xmore+xstart + - diagram_data["xnamesimg"][i]->xsize()/2), + (int)floor(diagram_data["ysize"]-ypos_for_xaxis+si/4.0)); + } + + //Write the text on the Y-axis + s=min(sizeof(diagram_data["ynamesimg"]), + sizeof(diagram_data["values_for_ynames"])); + for(int i=0; i<s; i++) + if ((diagram_data["values_for_ynames"][i]<=diagram_data["ymaxvalue"])&& + (diagram_data["values_for_ynames"][i]>=diagram_data["yminvalue"])) + { + barsdiagram->setcolor(@diagram_data["textcolor"]); + barsdiagram->paste_alpha_color( + diagram_data["ynamesimg"][i], + @(diagram_data["textcolor"]), + (int)floor(xpos_for_yaxis- + si/4.0-diagram_data["linewidth"]- + diagram_data["ynamesimg"][i]->xsize()), + (int)floor(-(diagram_data["values_for_ynames"][i]- + diagram_data["yminvalue"]) + *ymore+diagram_data["ysize"]-ystart + - + diagram_data["ymaxynames"]/2)); + + barsdiagram->setcolor(@diagram_data["axcolor"]); + barsdiagram-> + polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + xpos_for_yaxis-si/4, + (-(diagram_data["values_for_ynames"][i] + - diagram_data["yminvalue"])*ymore + + diagram_data["ysize"]-ystart), + + xpos_for_yaxis+si/4, + (-(diagram_data["values_for_ynames"][i] + - diagram_data["yminvalue"])*ymore + + diagram_data["ysize"]-ystart) + }), 1, 1)[0]); + } + + + //Write labels ({xstorhet, ystorhet, xenhet, yenhet}) + if (diagram_data["labelsize"]) + { + barsdiagram + ->paste_alpha_color(labelimg, + @(diagram_data["labelcolor"]), + diagram_data["xsize"]-labelx + - (int)ceil((float)diagram_data["linewidth"]), + diagram_data["ysize"] + - (int)ceil((float)(ypos_for_xaxis-si/2))); + + string label; + int x; + int y; + + if (diagram_data["labels"][3] && sizeof(diagram_data["labels"][3])) + label=diagram_data["labels"][1]+" ["+diagram_data["labels"][3]+"]"; //Yquantity + else + label=diagram_data["labels"][1]; + GETFONT(yaxisfont); + if ((label!="")&&(label!=0)) + labelimg=notext + ->write(label)->scale(0,diagram_data["labelsize"]); + else + labelimg=image(diagram_data["labelsize"],diagram_data["labelsize"]); + + if (labelimg->xsize()<1) + labelimg=image(diagram_data["labelsize"],diagram_data["labelsize"]); + + if (labelimg->xsize()> + diagram_data["xsize"]) + labelimg=labelimg->scale(diagram_data["xsize"], 0); + + x=max(2,((int)floor((float)xpos_for_yaxis)-labelimg->xsize()/2)); + x=min(x, barsdiagram->xsize()-labelimg->xsize()); + + y=0; + + if (label && sizeof(label)) + barsdiagram->paste_alpha_color(labelimg, + @(diagram_data["labelcolor"]), + x, + 2+labely-labelimg->ysize()); + } + + +#ifdef BG_DEBUG + }; +#endif + + diagram_data["ysize"]-=diagram_data["legend_size"]; + diagram_data["image"]=barsdiagram; + +#ifdef BG_DEBUG + diagram_data->bg_timers=bg_timers; +#endif + return diagram_data; +} diff --git a/lib/modules/Graphics.pmod/Graph.pmod/create_graph.pike b/lib/modules/Graphics.pmod/Graph.pmod/create_graph.pike new file mode 100755 index 0000000000000000000000000000000000000000..cfec894114cd8e2d0a595767e67f500efbf65b16 --- /dev/null +++ b/lib/modules/Graphics.pmod/Graph.pmod/create_graph.pike @@ -0,0 +1,1613 @@ +#!NOMODULE + +#include "graph.h" + +import Image; +import Array; +import Stdio; + +inherit "polyline.pike"; + +constant cvs_version = "$Id: create_graph.pike,v 1.1 1999/09/30 13:04:01 hedda Exp $"; + +/* + * name = "BG: Create graphs"; + * doc = "Business Graphics sub-module for drawing graphs."; + */ + +/* +These functions were written by Henrik "Hedda" Wallin (hedda@idonex.se) +Create_graph draws a graph but there are also some other functions +used by create_pie and create_bars. + +This was orginally a part of the Roxen module Business Graphics. +*/ + + +object tileimage(object img, int xs, int ys) +{ + //written by js@idonex.se + + object dest=image(xs,ys); + int srcx=img->xsize(); + int srcy=img->ysize(); + if(srcx <= 0 || srcy <= 0) + return dest; + + for(int x=0; x<=xs; x+=srcx) + for(int y=0; y<=ys; y+=srcy) + dest->paste(img,x,y); + + return dest; +} + +//Key word eng: +//This function writes a float like on an engineer-format +string diagram_eng(float a) +{ + string foo=""; + if (a<0.0) + { + foo="-"; + a=-a; + } + array(string) pfix = ({ "a", "f", "p", "n", "µ", "m", "", + "k", "M", "G", "T", "P", "E" }); + if (a == 0.0) return "0"; + float p = log(a)/log(1000.0); + if (p < -6.0) p = 0.0; + if (p >= 7.0) p = 0.0; + int i = (int) floor(p+0.000001); + string s; + sscanf(sprintf("%g%s", a*exp(-i*log(1000.0)), pfix[6+i]), "%*[ ]%s", s); + return foo+s; +} + +//Key word neng: +//This function writes a float like on an engineer-format +//Exept for 0.1<a<1.0 +// "neng" is a short for Noring's eng by the way :-) +string diagram_neng(float a) +{ + string foo=""; + if (a<0.0) + { + foo="-"; + a=-a; + } + array(string) pfix; + pfix = ({ "a", "f", "p", "n", "µ", "m", "", + "k", "M", "G", "T", "P", "E" }); + if (a == 0.0) return "0"; + float p = log(a)/log(1000.0); + if (p < -6.0) p = 0.0; + if (p >= 7.0) p = 0.0; + int i = (int) floor(p+0.000001); + if ((a<1.0)&&(a>=0.0999999)) + i=0; + string s; + sscanf(sprintf("%g%s", a*exp(-i*log(1000.0)), pfix[6+i]), "%*[ ]%s", s); + return foo+s; +} + +void draw(object(image) img, float h, array(float|string) coords, + void|int|float zerolength) +{ + if ((sizeof(coords)==2)|| + (sizeof(coords)==3)) + { + array(float) foo=({(float)coords[0]-(float)zerolength, + (float)coords[1], + (float)coords[0]+(float)zerolength, + (float)coords[1] + }); + img->polygone(make_polygon_from_line(h, foo,1, 1)[0]); + } + else + for(int i=0; i<sizeof(coords)-3; i+=2) + { + if (coords[i]!=VOIDSYMBOL) + if (coords[i+2]!=VOIDSYMBOL) + img->polygone( make_polygon_from_line(h, coords[i..i+3], 1, 1)[0] ); + else + if ( ((i>0)&&(coords[i-2]==VOIDSYMBOL))||(i==0) ) + img->polygone( make_polygon_from_line(h, coords[i..i+1], + 1, 1)[0] ); + } +} + +mapping(string:mixed) setinitcolors(mapping(string:mixed) diagram_data) +{ + foreach(diagram_data["data"], mixed* fo) + if (sizeof(fo)>diagram_data["datasize"]) + diagram_data["datasize"]=sizeof(fo); + + if (diagram_data["type"]=="sumbars") + for(int i; i<sizeof(diagram_data["data"]); i++) + diagram_data["data"][i]=diagram_data["data"][i]+ + allocate(diagram_data["datasize"]-sizeof(diagram_data["data"][i])); + + + if ((diagram_data["type"]=="sumbars")|| + (diagram_data["type"]=="bars")) + if (diagram_data["xdatanames"]) + if (sizeof(diagram_data["datasize"])< + sizeof(diagram_data["xdatanames"])) + diagram_data["xdatanames"]=diagram_data["xdatanames"] + [..sizeof(diagram_data["datasize"])-1]; + + object piediagram=diagram_data["image"]; + + if (diagram_data["datacolors"]) + { + int cnum; + if (diagram_data["type"]=="pie") + cnum=diagram_data["datasize"]; + else + cnum=sizeof(diagram_data["data"]); + if (sizeof(diagram_data["datacolors"])<cnum) + diagram_data["datacolors"]=0; + else + foreach(diagram_data["datacolors"], mixed color) + if (sizeof(color)!=3) + diagram_data["datacolors"]=0; + } + + if (!(diagram_data["datacolors"])) + { + int numbers; + if (diagram_data["type"]=="pie") + numbers=diagram_data["datasize"]; + else + numbers=sizeof(diagram_data["data"]); + + int** carr=allocate(numbers); + int steg=128+128/(numbers); + + switch( numbers ) { + case 1: + carr=({({19,200,102})}); + break; + case 2: + carr=({({190, 180, 0}), ({19, 19, 200})}); + break; + case 3: + carr=({({200, 19, 19}), ({19, 19, 200}), ({42, 200, 19})}); + break; + case 4: + carr=({({200, 19, 19}), ({19, 66, 200}), ({180, 180, 0}), ({19, 220, 102})}); + break; + case 5: + carr= ({({200, 19, 19}), ({19, 85, 200}), ({180, 180, 0}), ({129, 19, 200}), ({19, 200, 80})}); + break; + case 6: + carr= ({({200, 19, 19}), ({19, 85, 200}), ({180, 180, 0}), ({74, 220, 19}), ({100, 19, 200}), ({19, 200, 102})}); + break; + case 7: + carr= ({({200, 19, 19}), ({19, 85, 200}), ({180, 180, 0}), ({72, 19, 200}), ({74, 200, 19}), ({200, 19, 140}), ({19, 200, 102})}); + break; + case 8: + carr=({({200, 19, 19}), ({19, 110, 200}), ({180, 180, 0}), ({55, 19, 200}), ({96, 220, 19}), ({142, 19, 200}), ({19, 220, 69}), ({80, 19, 200})}) ; + break; + case 9: + carr= ({({200, 19, 19}), ({19, 115, 200}), ({200, 115, 19}), ({19, 19, 200}), ({118, 200, 19}), ({115, 19, 200}), ({42, 220, 19}), ({200, 19, 118}), ({19, 200, 112})}); + break; + case 10: + carr=({({200, 19, 19}), ({19, 121, 200}), ({200, 104, 19}), ({19, 55, 200}), ({140, 200, 19}), ({88, 19, 200}), ({74, 220, 19}), ({130, 24, 130}), ({19, 220, 69}), ({180, 180, 0})}) ; + break; + case 11: + carr=({({200, 19, 19}), ({19, 123, 200}), ({200, 99, 19}), ({19, 63, 200}), ({150, 200, 19}), ({74, 19, 200}), ({91, 220, 19}), ({134, 19, 200}), ({19, 200, 47}), ({200, 19, 115}), ({19, 200, 107})}) ; + break; + case 12: + carr=({({200, 19, 19}), ({19, 126, 200}), ({200, 93, 19}), ({19, 72, 200}), ({200, 148, 19}), ({61, 19, 200}), ({107, 200, 19}), ({115, 19, 200}), ({53, 220, 19}), ({200, 109, 140}), ({19, 220, 80}), ({200, 19, 185})}) ; + break; + default: + //No colours given! + //Now we have the %-numbers in pnumbers + //Lets create a colourarray carr + for(int i=0; i<numbers; i++) + carr[i]=Colors.hsv_to_rgb((i*steg)%256,190,200); + } + + if (diagram_data["bw"]) + for(int i=0; i<numbers; i++) + carr[i]=({ (i*steg)%256, + (i*steg)%256, + (i*steg)%256 }); + + diagram_data["datacolors"]=carr; + } + + diagram_data["image"]=piediagram; + return diagram_data["image"]; +} + + + +mapping(string:mixed) init(mapping(string:mixed) diagram_data) +{ + float xminvalue=0.0, xmaxvalue=-STORT, yminvalue=0.0, ymaxvalue=-STORT; + + if (diagram_data["xmin"]) + xminvalue=STORT; + if (diagram_data["ymin"]) + yminvalue=STORT; + + if (diagram_data["labelcolor"]==0) + diagram_data["labelcolor"]=diagram_data["textcolor"]; + if (diagram_data["labelcolor"] && sizeof(diagram_data["labelcolor"])!=3) + diagram_data["labelcolor"]=diagram_data["textcolor"]; + diagram_data["linewidth"]=(float)diagram_data["linewidth"]; + if ( diagram_data["linewidth"]< 0.01) + diagram_data["linewidth"]=1.0; + + //Oulinecolors + if ((diagram_data["backdatacolors"]==0)&& + (diagram_data["backlinewidth"])) + { + int dcs=sizeof(diagram_data["datacolors"]); + diagram_data["backdatacolors"]=allocate(dcs); + for(int i=0; i<dcs; i++) + diagram_data["backdatacolors"][i]=({255-diagram_data["datacolors"][i][0], + 255-diagram_data["datacolors"][i][1], + 255-diagram_data["datacolors"][i][2] + }); + + } + //diagram_data["backlinewidth"]=diagram_data["linewidth"]+1.0; + + if (!(diagram_data["legendcolor"])) + diagram_data["legendcolor"]=diagram_data["bgcolor"]; + + if (diagram_data["type"]=="graph") + diagram_data["subtype"]="line"; + + if (diagram_data["type"]=="bars") + diagram_data["xminvalue"]=0; + + if (diagram_data["type"]=="sumbars") + { + diagram_data["xminvalue"]=0; + if (diagram_data["subtype"]=="norm") + { + diagram_data["yminvalue"]=0; + if (!(diagram_data["labels"])) + diagram_data["labels"]=({"", "%", "", ""}); + } + } + if ((diagram_data["subtype"]==0) || + (diagram_data["subtype"]=="")) + diagram_data["subtype"]="line"; + + if (diagram_data["subtype"]=="line") + if ((!(diagram_data["drawtype"])) || + (diagram_data["drawtype"]=="")) + diagram_data["drawtype"]="linear"; + + if (diagram_data["subtype"]=="box") + if ((!(diagram_data["drawtype"])) || + (diagram_data["drawtype"]=="")) + diagram_data["drawtype"]="2D"; + + if (diagram_data["type"]=="sumbars") + { + diagram_data["data"]=Array.map(diagram_data["data"], replace, + VOIDSYMBOL, 0.0); + + int j=sizeof(diagram_data["data"]); + float k; + if (diagram_data["subtype"]=="norm") + { + int j2=diagram_data["datasize"]; + for(int i=0; i<j2; i++) + { + k=`+(@column(diagram_data["data"], i)); + if (k<LITET) + k=LITET; + else + k=100.0/k; + for(int i2=0; i2<j; i2++) + diagram_data["data"][i2][i]*=k; + } + yminvalue=0.0; + ymaxvalue=100.0; + + } + else + for(int i=0; i<diagram_data["datasize"]; i++) + { + if (yminvalue>(k=`+(@column(diagram_data["data"], i)))) + yminvalue=k; + if (ymaxvalue<(k)) + ymaxvalue=k; + } + xminvalue=0.0; + xmaxvalue=10.0; + + } + else + foreach(diagram_data["data"], array(float|string) d) + { + int j=sizeof(d); + + if (diagram_data["type"]=="graph") + { + d-=({VOIDSYMBOL}); + j=sizeof(d)-1; + for(int i=0; i<j; i++) + { + float k; + if (xminvalue>(k=d[i])) + xminvalue=k; + if (xmaxvalue<(k)) + xmaxvalue=k; + if (yminvalue>(k=d[++i])) + yminvalue=k; + if (ymaxvalue<(k)) + ymaxvalue=k; + } + } + else + if (diagram_data["type"]=="bars") + { + for(int i; i<j; i++) + if (floatp(d[i])) + { + float k; + if (yminvalue>(k=d[i])) + yminvalue=k; + if (ymaxvalue<(k)) + ymaxvalue=k; + } + xminvalue=0.0; + xmaxvalue=10.0; + } + else + if (diagram_data["type"]=="pie") + { + for(int i; i<j; i++) + { + if (!floatp(d[i])) + d[i]=0; + + float k; + if (yminvalue>(k=d[i])) + yminvalue=k; + if (ymaxvalue<(k)) + ymaxvalue=k; + } + xminvalue=0.0; + xmaxvalue=10.0; + } + else + throw( ({"\""+diagram_data["type"] + + "\" is an unknown graph type!\n", backtrace()})); + } + + if (diagram_data["type"]=="sumbars") + diagram_data["box"]=0; + + if (diagram_data["type"]=="pie") + { + diagram_data["vertgrid"]=0; + diagram_data["horgrid"]=0; + } + + xmaxvalue=max(xmaxvalue, xminvalue+STORTLITET); + ymaxvalue=max(ymaxvalue, yminvalue+STORTLITET); + + + if (!(diagram_data["xminvalue"])) + diagram_data["xminvalue"]=xminvalue; + if ((!(diagram_data["xmaxvalue"])) || + (diagram_data["xmaxvalue"]<xmaxvalue)) + if (xmaxvalue<0.0) + diagram_data["xmaxvalue"]=0.0; + else + diagram_data["xmaxvalue"]=xmaxvalue; + if (!(diagram_data["yminvalue"])) + diagram_data["yminvalue"]=yminvalue; + if ((!(diagram_data["ymaxvalue"])) || + (diagram_data["ymaxvalue"]<ymaxvalue)) + if (ymaxvalue<0.0) + diagram_data["ymaxvalue"]=0.0; + else + diagram_data["ymaxvalue"]=ymaxvalue; + + //Create empty names on xnames if the names don't exist. + if ((diagram_data["type"]=="bars")||(diagram_data["type"]=="sumbars")) + { + if (!(diagram_data["xnames"])) + diagram_data["xnames"]=allocate(diagram_data["datasize"]); + } + + //If xnames exist, set xspace if values_for_xnames don't exist + if (diagram_data["xnames"] && sizeof(diagram_data["xnames"])==0) + diagram_data["xnames"]=0; + if (diagram_data["type"]!="graph") + if (diagram_data["xnames"]) + diagram_data["xspace"]=max((diagram_data["xmaxvalue"]- + diagram_data["xminvalue"]) + /(float)(max(sizeof(diagram_data["xnames"]), + diagram_data["datasize"])), + LITET*20); + + //If ynames exist, set yspace + if (diagram_data["ynames"]) + { + diagram_data["ynames"]=({" "})+diagram_data["ynames"]; + diagram_data["yspace"]=(diagram_data["ymaxvalue"]- + diagram_data["yminvalue"]) + /(float)sizeof(diagram_data["ynames"]); + } + //Check if labelsize is to big: + if (diagram_data["labelsize"]>diagram_data["ysize"]/5) + diagram_data["labelsize"]=diagram_data["ysize"]/5; + + return diagram_data; +}; + +/* +#ifndef ROXEN +object get_font(string j, int p, int t, int h, string fdg, int s, int hd) +{ + return font()->load("avant_garde"); +}; +#endif +*/ + +//Paste the text into the graph +//calculate xmaxynames, ymaxynames xmaxxnames ymaxxnames +mapping(string:mixed) create_text(mapping(string:mixed) diagram_data) +{ + int tobig=1; + int xmaxynames=0, ymaxynames=0, xmaxxnames=0, ymaxxnames=0; + int r=0; + while(tobig) + { + r++; + if (r>9) + throw( ({"Very bad error while trying to resize the textfont!\n", + backtrace()})); + + GETFONT(xnamesfont); + int j; + diagram_data["xnamesimg"]=allocate(j=sizeof(diagram_data["xnames"])); + for(int i=0; i<j; i++) + { + if ( ((diagram_data["values_for_xnames"][i]>LITET) + || (diagram_data["values_for_xnames"][i]<-LITET)) + && ((diagram_data["xnames"][i]) + && sizeof(diagram_data["xnames"][i]))) + diagram_data["xnamesimg"][i]=notext + ->write(UNICODE(diagram_data["xnames"][i],diagram_data["encoding"])) + ->scale(0,diagram_data["fontsize"]); + else + diagram_data["xnamesimg"][i]= + image(diagram_data["fontsize"],diagram_data["fontsize"]); + + if (diagram_data["xnamesimg"][i]->xsize()<1) + diagram_data["xnamesimg"][i]=image(diagram_data["fontsize"], + diagram_data["fontsize"]); + } + + GETFONT(ynamesfont); + + diagram_data["ynamesimg"]=allocate(j=sizeof(diagram_data["ynames"])); + if ((diagram_data["type"]=="bars")|| + (diagram_data["type"]=="sumbars")) + for(int i=0; i<j; i++) + { + if ((diagram_data["ynames"][i]) && (sizeof(diagram_data["ynames"][i]))) + { + if (diagram_data["ynames"][i]=="-0") + diagram_data["ynames"][i]="0"; + diagram_data["ynamesimg"][i]=notext + ->write(UNICODE(diagram_data["ynames"][i], + diagram_data["encoding"])) + ->scale(0,diagram_data["fontsize"]); + } + else + diagram_data["ynamesimg"][i]= + image(diagram_data["fontsize"],diagram_data["fontsize"]); + + if (diagram_data["ynamesimg"][i]->xsize()<1) + diagram_data["ynamesimg"][i]=image(diagram_data["fontsize"], + diagram_data["fontsize"]); + } + else + for(int i=0; i<j; i++) + { + if (((diagram_data["values_for_ynames"][i]>LITET) + || (diagram_data["values_for_ynames"][i]<-LITET)) + && ((diagram_data["ynames"][i]) + && (sizeof(diagram_data["ynames"][i])))) + diagram_data["ynamesimg"][i]=notext + ->write(UNICODE(diagram_data["ynames"][i], + diagram_data["encoding"])) + ->scale(0,diagram_data["fontsize"]); + else + diagram_data["ynamesimg"][i]= + image(diagram_data["fontsize"],diagram_data["fontsize"]); + + if (diagram_data["ynamesimg"][i]->xsize()<1) + diagram_data["ynamesimg"][i]=image(diagram_data["fontsize"], + diagram_data["fontsize"]); + } + + if (diagram_data["orient"]=="vert") + for(int i; i<sizeof(diagram_data["xnamesimg"]); i++) + diagram_data["xnamesimg"][i]=diagram_data["xnamesimg"][i] + ->rotate_ccw(); + + + xmaxynames=0, ymaxynames=0, xmaxxnames=0, ymaxxnames=0; + + foreach(diagram_data["xnamesimg"], object img) + if (img->ysize()>ymaxxnames) + ymaxxnames=img->ysize(); + + foreach(diagram_data["xnamesimg"], object img) + if (img->xsize()>xmaxxnames) + xmaxxnames=img->xsize(); + + foreach(diagram_data["ynamesimg"], object img) + if (img->ysize()>ymaxynames) + ymaxynames=img->ysize(); + + foreach(diagram_data["ynamesimg"], object img) + if (img->xsize()>xmaxynames) + xmaxynames=img->xsize(); + + diagram_data["ymaxxnames"]=ymaxxnames; + diagram_data["xmaxxnames"]=xmaxxnames; + diagram_data["ymaxynames"]=ymaxynames; + diagram_data["xmaxynames"]=xmaxynames; + + if (ymaxxnames+xmaxynames>diagram_data["ysize"]/2) + { + tobig+=2; + diagram_data["fontsize"]=diagram_data["fontsize"] + * diagram_data["ysize"]/2/(ymaxxnames+xmaxynames); + } + + if (ymaxynames>diagram_data["ysize"]/3) + { + tobig+=2; + diagram_data["fontsize"]=diagram_data["fontsize"] + * diagram_data["ysize"]/3/ymaxynames; + } + + if (xmaxynames>diagram_data["xsize"]/2) + { + tobig+=2; + diagram_data["fontsize"]=diagram_data["fontsize"] + * diagram_data["xsize"]/2/xmaxynames; + } + + if (xmaxxnames>diagram_data["xsize"]/3) + { + tobig+=2; + diagram_data["fontsize"]=diagram_data["fontsize"] + * diagram_data["xsize"]/3/xmaxxnames; + } + + if (tobig==1) + tobig=0; + else + tobig--; + + } +} + +//Remove ending zeros. +//The Swedish is some insults between the programmers +string no_end_zeros(string f) +{ + if (search(f, ".")!=-1) + { + int j; + /* FIXME Vad i Hvte gör du här?! */ + //Jag vet vad jag gör! Idi! :-) + for(j=sizeof(f)-1; f[j]=='0'; j--) + {} + if (f[j]=='.') + return f[..--j]; + else + return f[..j]; + } + return f; +} + +//This functions just happen to draw the grid... +mapping draw_grid(mapping diagram_data, int|float xpos_for_yaxis, + int|float ypos_for_xaxis, float xmore, float ymore, + float xstart, float ystart, float si) +{ + //Draw the vertical grid (vertgrid) + int s=sizeof(diagram_data["xnames"]); + object graph=diagram_data["image"]; + if (!diagram_data["gridwidth"]) + diagram_data["gridwidth"]=diagram_data["linewidth"]/4.0; + + graph->setcolor(@diagram_data["gridcolor"]); + float gw=(float)diagram_data["gridwidth"]; + if ((diagram_data["vertgrid"])&& + (gw>LITET)) + { + mixed vfx=diagram_data["values_for_xnames"]; + float ystop=(float)(diagram_data["ysize"]-diagram_data["ystop"]); + float ystart=(float)(diagram_data["ysize"]-ystart); + float gw2=gw/2.0; + float xmin=(float)diagram_data["xminvalue"]; + float xmax=(float)diagram_data["xmaxvalue"]; + for(int i=0; i<s; i++) + if ((vfx[i]>xmin)&& + (vfx[i]<xmax)) + { + float temp; + graph-> + polygone(({ + (temp=(vfx[i]- diagram_data["xminvalue"])* xmore+xstart)-gw2, + ystart, + + temp+gw2, + ystart, + + temp+gw2, + ystop, + + temp-gw2, + ystop})); + /* + //make_polygon_from_line() can't handle close edges + make_polygon_from_line( + gw, + ({ + ((diagram_data["values_for_xnames"][i] + - diagram_data["xminvalue"]) + * xmore+xstart), + diagram_data["ysize"]-ystart, + + ((diagram_data["values_for_xnames"][i] + - diagram_data["xminvalue"]) + * xmore+xstart), + diagram_data["ysize"] + - diagram_data["ystop"] + }), + 1, 1)[0]); + */ + } + } + //Draw the horisontal grid (the horgrid) + s=sizeof(diagram_data["values_for_ynames"]); + if ((diagram_data["horgrid"]) + && (gw>LITET)) + for(int i=0; i<s; i++) + if ((diagram_data["values_for_ynames"][i]>diagram_data["yminvalue"])&& + (diagram_data["values_for_ynames"][i]<diagram_data["ymaxvalue"])) + { + graph-> + polygone(make_polygon_from_line( + gw, + ({ + xstart, + (-(diagram_data["values_for_ynames"][i]- + diagram_data["yminvalue"]) + *ymore+diagram_data["ysize"]-ystart), + + diagram_data["xstop"], + (-(diagram_data["values_for_ynames"][i]- + diagram_data["yminvalue"]) + *ymore+diagram_data["ysize"]- + ystart) + }), + 1, 1)[0]); + } +} + +//This function also paste the info in the legend. +mapping set_legend_size(mapping diagram_data) +{ + if (!(diagram_data["legendfontsize"])) + diagram_data["legendfontsize"]=diagram_data["fontsize"]; + int raws; + //Check if the font is to big: + int tobig=1; + int j=0; + int xmax=0, ymax=0; + int b; + int columnnr; + array(object(image)) texts; + array(mixed) plupps; //This is the stuff that is draw before the texts + object notext; + + + if (diagram_data["legend_texts"]) + { + if (sizeof(diagram_data["legend_texts"])> + sizeof(diagram_data["datacolors"])) + diagram_data["legend_texts"]=diagram_data["legend_texts"] + [..sizeof(diagram_data["datacolors"])-1]; + + int r=0; + while(tobig) + { + r++; + if (r>3) + throw( ({"Very bad error while trying to resize the legendfonts!\n", + backtrace()})); + { + texts=allocate(sizeof(diagram_data["legend_texts"])); + plupps=allocate(sizeof(diagram_data["legend_texts"])); + + GETFONT(legendfont); + + j=sizeof(texts); + if (!diagram_data["legendcolor"]) + diagram_data["legendcolor"]=diagram_data["bgcolor"]; + for(int i=0; i<j; i++) + { + if (diagram_data["legend_texts"][i] + && (sizeof(diagram_data["legend_texts"][i]))) + texts[i]=notext + ->write(UNICODE(diagram_data["legend_texts"][i], + diagram_data["encoding"])) + ->scale(0,diagram_data["legendfontsize"]); + else + texts[i]= + image(diagram_data["legendfontsize"], + diagram_data["legendfontsize"]); + + if (texts[i]->xsize()<1) + texts[i]=image(diagram_data["legendfontsize"], + diagram_data["legendfontsize"]); + } + + xmax=0, ymax=0; + + foreach(texts, object img) + if (img->ysize()>ymax) + ymax=img->ysize(); + + foreach(texts, object img) + if (img->xsize()>xmax) + xmax=img->xsize(); + + //Draw the line for Graph / the box for bars + if ((diagram_data["type"]=="graph") || + (diagram_data["type"]=="bars") || + (diagram_data["type"]=="sumbars") || + (diagram_data["type"]=="pie")) + for(int i=0; i<j; i++) + { + plupps[i]=image(diagram_data["legendfontsize"], + diagram_data["legendfontsize"]); + + plupps[i]->setcolor(255,255,255); + if ( (diagram_data["linewidth"]*1.5 + < (float)diagram_data["legendfontsize"]) + && (diagram_data["subtype"]=="line") + && (diagram_data["drawtype"]!="level") ) + plupps[i]->polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + (float)(diagram_data["linewidth"]/2+1), + (float)(plupps[i]->ysize()- + diagram_data["linewidth"]/2-2), + (float)(plupps[i]->xsize()- + diagram_data["linewidth"]/2-2), + (float)(diagram_data["linewidth"]/2+1) + }), + 1, 1)[0]); + else + plupps[i]->box( 1, 1, plupps[i]->xsize()-2, + plupps[i]->ysize()-2 ); + } + else + throw( ({"\""+diagram_data["type"]+"\" is an unknown graph type!\n", + backtrace()})); + + //Calculate how many colomns it can be + b; + columnnr=(diagram_data["image"]->xsize()-4)/ + (b=xmax+2*diagram_data["legendfontsize"]); + + if (columnnr==0) + { + int m=((diagram_data["image"]->xsize()-4)-2*diagram_data["legendfontsize"]); + if (m<4) m=4; + for(int i=0; i<sizeof(texts); i++) + if (texts[i]->xsize()>m) + { + texts[i]= + texts[i]->scale((int)m,0); + //write("x: "+texts[i]->xsize()+"\n"); + //write("y: "+texts[i]->ysize()+"\n"); + } + columnnr=1; + } + + raws=(j+columnnr-1)/columnnr; + diagram_data["legend_size"]=raws*diagram_data["legendfontsize"]; + + + if (diagram_data["image"]->ysize()/2>=raws + * diagram_data["legendfontsize"]) + tobig=0; + else + { + tobig++; + if (tobig==2) + diagram_data["legendfontsize"]=diagram_data["image"]->ysize()/raws; + else + diagram_data["legendfontsize"]=diagram_data["image"] + -> ysize()/2/raws; + } + } + + } + } + + //Place images and texts + + if (diagram_data["legend_texts"]) + { + for(int i=0; i<j; i++) + { + diagram_data["image"] + ->paste_alpha_color(plupps[i], + @(diagram_data["datacolors"][i]), + (i/raws)*b, + (i%raws)*diagram_data["legendfontsize"] + +diagram_data["image"] + ->ysize()-diagram_data["legend_size"] ); + diagram_data["image"]->setcolor(0,0,0); + draw( diagram_data["image"], 0.5, + ({(i/raws)*b, (i%raws)*diagram_data["legendfontsize"]+ + diagram_data["image"]->ysize()-diagram_data["legend_size"]+1, + (i/raws)*b+plupps[i]->xsize()-1.0, + (i%raws)*diagram_data["legendfontsize"] + + diagram_data["image"]->ysize()-diagram_data["legend_size"]+1, + (i/raws)*b+plupps[i]->xsize()-1.0, + (i%raws)*diagram_data["legendfontsize"] + + diagram_data["image"]->ysize()-diagram_data["legend_size"] + + plupps[i]->ysize()-1, + (i/raws)*b+1, + (i%raws)*diagram_data["legendfontsize"] + diagram_data["image"] + -> ysize()-diagram_data["legend_size"]+plupps[i]->ysize()-1, + (i/raws)*b, (i%raws)*diagram_data["legendfontsize"] + + diagram_data["image"]->ysize()-diagram_data["legend_size"]+1 + })); + + + diagram_data["image"] + ->paste_alpha_color(texts[i], + @(diagram_data["textcolor"]), + (i/raws)*b+1+diagram_data["legendfontsize"], + (i%raws)*diagram_data["legendfontsize"]+ + diagram_data["image"]->ysize() + - diagram_data["legend_size"] ); + } + } + else + diagram_data["legend_size"]=0; +} + +mapping(string:mixed) init_bg(mapping diagram_data) +{ + if (diagram_data["bgcolor"]) + diagram_data["image"]=image(diagram_data["xsize"],diagram_data["ysize"], + @(diagram_data["bgcolor"])); + else + if ((diagram_data["xsize"]==0)||(0==diagram_data["ysize"])) + { + diagram_data["xsize"]=diagram_data["image"]->xsize(); + diagram_data["ysize"]=diagram_data["image"]->ysize(); + } + else + if (diagram_data["image"]&&(diagram_data["image"]->xsize()>4)&& + (diagram_data["image"]->ysize()>4)) + diagram_data["image"]=tileimage(diagram_data["image"], + diagram_data["xsize"], + diagram_data["ysize"]); + else + diagram_data["image"]=image(diagram_data["xsize"], + diagram_data["ysize"], + 255,255,255); +} + +int write_name(mapping diagram_data) +{ + if (!diagram_data["name"]) + return 0; + GETFONT(namefont); + + object text; + int y,x; + if (diagram_data["namesize"]) + y=diagram_data["namesize"]; + else + y=diagram_data["fontsize"]; + + text=notext->write(UNICODE(diagram_data["name"],diagram_data["encoding"])) + ->scale(0,y); + + if (text->xsize()>=diagram_data["xsize"]) + text->scale(diagram_data["xsize"]-1,0); + + array color; + if (diagram_data["namecolor"]) + color=diagram_data["namecolor"]; + else + color=diagram_data["textcolor"]; + + + diagram_data["image"] + -> paste_alpha_color( text, @(color), + diagram_data["xsize"]/2-text->xsize()/2, 2 ); + + return text->ysize()+2; +} + +mapping(string:mixed) create_graph(mapping diagram_data) +{ + //Supports only xsize>=100 + int si=diagram_data["fontsize"]; + + string where_is_ax; + + //No uneven data + + for(int i=0; i<sizeof(diagram_data["data"]); i++) + if (sizeof(diagram_data["data"][i])%2) + diagram_data["data"][i] + = diagram_data["data"][i][..sizeof(diagram_data["data"][i])-2]; + + //Fix defaultcolors! + setinitcolors(diagram_data); + + object(image) graph; + init_bg(diagram_data); + graph=diagram_data["image"]; + + set_legend_size(diagram_data); + + diagram_data["ysize"]-=diagram_data["legend_size"]; + + //Get biggest and smallest data-value + init(diagram_data); + + //Find out how many and how big the textimages are + if (!(diagram_data["xspace"])) + if (diagram_data["xnames"]) + { + diagram_data["xnames"]=({" "})+diagram_data["xnames"]; + diagram_data["xspace"]=(diagram_data["xmaxvalue"] + - diagram_data["xminvalue"])/ + (LITET+sizeof(diagram_data["xnames"])); + } + else + { + //Initiate how long it is between + float range=(diagram_data["xmaxvalue"] + - diagram_data["xminvalue"]); + if ((range>-LITET)&& + (range<LITET)) + range=LITET*10.0; + + float space=pow(10.0, floor(log(range/3.0)/log(10.0))); + if ( range/space > 5.0 ) + { + if(range/(2.0*space)>5.0) + space *= 5.0; + else + space *= 2.0; + } + else + if (range/space<2.5) + space *= 0.5; + diagram_data["xspace"]=space; + } + if (!(diagram_data["yspace"])) + { + //Initiate how long it is between + + float range=(diagram_data["ymaxvalue"] + - diagram_data["yminvalue"]); + if ((range>-LITET)&& + (range<LITET)) + range=LITET*10.0; + + float space=pow(10.0, floor(log(range/3.0)/log(10.0))); + if (range/space>5.0) + if(range/(2.0*space)>5.0) + space *= 5.0; + else + space *= 2.0; + else + if (range/space<2.5) + space *= 0.5; + diagram_data["yspace"]=space; + } + + if (!(diagram_data["values_for_xnames"])) + { + if ((diagram_data["xspace"]<LITET)&& + (diagram_data["xspace"]>-LITET)) + throw( ({"Very bad error because xspace is zero!\n", backtrace()})); + float start; + start=diagram_data["xminvalue"]; + start=diagram_data["xspace"]*ceil((start)/diagram_data["xspace"]); + diagram_data["values_for_xnames"]=({start}); + while(diagram_data["values_for_xnames"][-1] + <= diagram_data["xmaxvalue"]-diagram_data["xspace"]) + diagram_data["values_for_xnames"]+=({start+=diagram_data["xspace"]}); + } + if (!(diagram_data["values_for_ynames"])) + { + if ((diagram_data["yspace"]<LITET)&& + (diagram_data["yspace"]>-LITET)) + throw( ({"Very bad error because yspace is zero!\n", backtrace()})); + + float start; + start=diagram_data["yminvalue"]; + start=diagram_data["yspace"]*ceil((start)/diagram_data["yspace"]); + diagram_data["values_for_ynames"]=({start}); + while(diagram_data["values_for_ynames"][-1] + <= diagram_data["ymaxvalue"]-diagram_data["yspace"]) + diagram_data["values_for_ynames"]+=({start+=diagram_data["yspace"]}); + } + + //Generate the texten if it doesn't exist + if (!(diagram_data["ynames"])) + if (diagram_data["eng"]) + { + diagram_data["ynames"]= + allocate(sizeof(diagram_data["values_for_ynames"])); + + for(int i=0; i<sizeof(diagram_data["values_for_ynames"]); i++) + diagram_data["ynames"][i]= + diagram_eng((float)(diagram_data["values_for_ynames"][i])); + } + else if (diagram_data["neng"]) + { + diagram_data["ynames"]= + allocate(sizeof(diagram_data["values_for_ynames"])); + + for(int i=0; i<sizeof(diagram_data["values_for_ynames"]); i++) + diagram_data["ynames"][i]= + diagram_neng((float)(diagram_data["values_for_ynames"][i])); + } else { + diagram_data["ynames"]= + allocate(sizeof(diagram_data["values_for_ynames"])); + + for(int i=0; i<sizeof(diagram_data["values_for_ynames"]); i++) + diagram_data["ynames"][i]= + no_end_zeros((string)(diagram_data["values_for_ynames"][i])); + } + + + + if (!(diagram_data["xnames"])) + if (diagram_data["eng"]) + { + diagram_data["xnames"]= + allocate(sizeof(diagram_data["values_for_xnames"])); + + for(int i=0; i<sizeof(diagram_data["values_for_xnames"]); i++) + diagram_data["xnames"][i]= + diagram_eng((float)(diagram_data["values_for_xnames"][i])); + } + else if (diagram_data["neng"]) + { + diagram_data["xnames"]= + allocate(sizeof(diagram_data["values_for_xnames"])); + + for(int i=0; i<sizeof(diagram_data["values_for_xnames"]); i++) + diagram_data["xnames"][i]= + diagram_neng((float)(diagram_data["values_for_xnames"][i])); + } + else + { + diagram_data["xnames"]= + allocate(sizeof(diagram_data["values_for_xnames"])); + + for(int i=0; i<sizeof(diagram_data["values_for_xnames"]); i++) + diagram_data["xnames"][i]= + no_end_zeros((string)(diagram_data["values_for_xnames"][i])); + } + + + //Draw images with the texts + //calculate xmaxynames, ymaxynames xmaxxnames ymaxxnames + create_text(diagram_data); + si=diagram_data["fontsize"]; + + //Create the labeltext for the xaxis + object labelimg; + string label; + int labelx=0; + int labely=0; + + GETFONT(xaxisfont); + + if (diagram_data["labels"]) + { + if (diagram_data["labels"][2] && sizeof(diagram_data["labels"][2])) + label=diagram_data["labels"][0]+" ["+diagram_data["labels"][2]+"]"; //Xstorhet + else + label=diagram_data["labels"][0]; + if ((label!="")&&(label!=0)) + labelimg=notext + -> write(label)->scale(0,diagram_data["labelsize"]); + else + labelimg=image(diagram_data["labelsize"],diagram_data["labelsize"]); + + if (labelimg->xsize()<1) + labelimg=image(diagram_data["labelsize"],diagram_data["labelsize"]); + + if (labelimg->xsize()> + diagram_data["xsize"]/2) + labelimg=labelimg->scale(diagram_data["xsize"]/2,0); + + labely=diagram_data["labelsize"]; + labelx=labelimg->xsize(); + } + + labely+=write_name(diagram_data); + + int ypos_for_xaxis; //Distance from below + int xpos_for_yaxis; //Distanse from right + //Calculate where in the image a graph should be drawn + diagram_data["ystart"]=(int)ceil(diagram_data["linewidth"]); + diagram_data["ystop"]=diagram_data["ysize"] + - (int)ceil((float)diagram_data["linewidth"]+si)-labely; + if (((float)diagram_data["yminvalue"]>-LITET)&& + ((float)diagram_data["yminvalue"]<LITET)) + diagram_data["yminvalue"]=0.0; + + if (diagram_data["yminvalue"]<0) + { + //Calculate position for the x-axis. + //If this doesn't work: Draw the x-axis at bottom or top + // and recalculate diagram_data["ystart"] + ypos_for_xaxis=((-diagram_data["yminvalue"])*(diagram_data["ystop"]-diagram_data["ystart"]))/ + (diagram_data["ymaxvalue"]-diagram_data["yminvalue"])+diagram_data["ystart"]; + + int minpos; + minpos=max(labely, diagram_data["ymaxxnames"])+si/2; + if (minpos>ypos_for_xaxis) + { + ypos_for_xaxis=minpos; + diagram_data["ystart"]=ypos_for_xaxis+ + diagram_data["yminvalue"]*(diagram_data["ystop"]-ypos_for_xaxis)/ + (diagram_data["ymaxvalue"]); + } else { + int maxpos; + maxpos=diagram_data["ysize"]- + (int)ceil(diagram_data["linewidth"]+si*2)- + labely; + if (maxpos<ypos_for_xaxis) + { + ypos_for_xaxis=maxpos; + diagram_data["ystop"]=ypos_for_xaxis+ + diagram_data["ymaxvalue"]*(ypos_for_xaxis-diagram_data["ystart"])/ + (0-diagram_data["yminvalue"]); + } + } + } + else + if (diagram_data["yminvalue"]==0.0) + { + // Place the x-axis and diagram_data["ystart"] at bottom. + diagram_data["ystop"]=diagram_data["ysize"]- + (int)ceil(diagram_data["linewidth"]+si)-labely; + ypos_for_xaxis=max(labely, diagram_data["ymaxxnames"])+si/2; + diagram_data["ystart"]=ypos_for_xaxis; + } + else + { + //Place the x-axis at bottom and diagram_data["ystart"] a + //Little bit higher + diagram_data["ystop"]=diagram_data["ysize"]- + (int)ceil(diagram_data["linewidth"]+si)-labely; + ypos_for_xaxis=max(labely, diagram_data["ymaxxnames"])+si/2; + diagram_data["ystart"]=ypos_for_xaxis+si*2; + } + + //Calculate position for the y-axis + diagram_data["xstart"]=(int)ceil(diagram_data["linewidth"]); + diagram_data["xstop"]=diagram_data["xsize"]- + (int)ceil(diagram_data["linewidth"])-max(si,labelx+si/2) + - diagram_data["xmaxxnames"]/2; + if (((float)diagram_data["xminvalue"]>-LITET)&& + ((float)diagram_data["xminvalue"]<LITET)) + diagram_data["xminvalue"]=0.0; + + if (diagram_data["xminvalue"]<0.0) + { + //Calculate position for the y-axis. + //If this doesn't work: Draw the y-axis at right or left + // and recalculate diagram_data["xstart"] + xpos_for_yaxis=((-diagram_data["xminvalue"])*(diagram_data["xstop"] + - diagram_data["xstart"]))/ + (diagram_data["xmaxvalue"]-diagram_data["xminvalue"]) + + diagram_data["xstart"]; + + int minpos; + minpos=diagram_data["xmaxynames"]+si/2+ + diagram_data["linewidth"]+2; + if (minpos>xpos_for_yaxis) + { + xpos_for_yaxis=minpos; + diagram_data["xstart"]=xpos_for_yaxis+ + diagram_data["xminvalue"]*(diagram_data["xstop"]-xpos_for_yaxis)/ + (diagram_data["ymaxvalue"]); + } else { + int maxpos; + maxpos=diagram_data["xsize"]- + (int)ceil(diagram_data["linewidth"])-si*2-labelx; + if (maxpos<xpos_for_yaxis) + { + xpos_for_yaxis=maxpos; + diagram_data["xstop"]=xpos_for_yaxis+ + diagram_data["xmaxvalue"]*(xpos_for_yaxis-diagram_data["xstart"])/ + (0-diagram_data["xminvalue"]); + } + } + } + else + if (diagram_data["xminvalue"]==0.0) + { + //Place the y-axis at left (?) and diagram_data["xstart"] at + // the same place + diagram_data["xstop"]=diagram_data["xsize"] + - (int)ceil(diagram_data["linewidth"])-max(si,labelx+si/2)-diagram_data["xmaxxnames"]/2; + xpos_for_yaxis=diagram_data["xmaxynames"]+si/2+ + diagram_data["linewidth"]+2; + diagram_data["xstart"]=xpos_for_yaxis; + } else { + //Place the y-axis at left (?) and diagram_data["xstart"] a + // little bit more right. + diagram_data["xstop"]=diagram_data["xsize"]- + (int)ceil(diagram_data["linewidth"])-max(si,labelx+si/2) + - diagram_data["xmaxxnames"]/2; + xpos_for_yaxis=diagram_data["xmaxynames"]+si/2+ + diagram_data["linewidth"]+2; + diagram_data["xstart"]=xpos_for_yaxis+si*2; + } + + //draw the axises. + graph->setcolor(@(diagram_data["axcolor"])); + + //Draw the x-axis + if ((diagram_data["xminvalue"]<=LITET)&& + (diagram_data["xmaxvalue"]>=-LITET)) + graph-> + polygone(make_polygon_from_line(diagram_data["linewidth"], + ({ + diagram_data["linewidth"], + diagram_data["ysize"]- ypos_for_xaxis, + diagram_data["xsize"]- + si-labelx/2, + diagram_data["ysize"]-ypos_for_xaxis + }), + 1, 1)[0]); + else + if (diagram_data["xmaxvalue"]<-LITET) + { + graph-> + polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + diagram_data["linewidth"], + diagram_data["ysize"]- ypos_for_xaxis, + + xpos_for_yaxis-4.0/3.0*si, + diagram_data["ysize"]-ypos_for_xaxis, + + xpos_for_yaxis-si, + diagram_data["ysize"]-ypos_for_xaxis- + si/2.0, + xpos_for_yaxis-si/1.5, + diagram_data["ysize"]-ypos_for_xaxis+ + si/2.0, + + xpos_for_yaxis-si/3.0, + diagram_data["ysize"]-ypos_for_xaxis, + + diagram_data["xsize"]-si-labelx/2, + diagram_data["ysize"]-ypos_for_xaxis + + }), + 1, 1)[0]); + } + else + if (diagram_data["xminvalue"]>LITET) + { + graph-> + polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + diagram_data["linewidth"], + diagram_data["ysize"]- ypos_for_xaxis, + + xpos_for_yaxis+si/3.0, + diagram_data["ysize"]-ypos_for_xaxis, + + xpos_for_yaxis+si/1.5, + diagram_data["ysize"]-ypos_for_xaxis- + si/2.0, + xpos_for_yaxis+si, + diagram_data["ysize"]-ypos_for_xaxis+ + si/2.0, + + xpos_for_yaxis+4.0/3.0*si, + diagram_data["ysize"]-ypos_for_xaxis, + + diagram_data["xsize"]-si-labelx/2, + diagram_data["ysize"]-ypos_for_xaxis + + }), + 1, 1)[0]); + + } + + graph->polygone( + ({ + diagram_data["xsize"]- + diagram_data["linewidth"]/2- + (float)si-labelx/2, + diagram_data["ysize"]-ypos_for_xaxis- + (float)si/4.0, + + diagram_data["xsize"]- + diagram_data["linewidth"]/2-labelx/2, + diagram_data["ysize"]-ypos_for_xaxis, + + diagram_data["xsize"]- + diagram_data["linewidth"]/2- + (float)si-labelx/2, + diagram_data["ysize"]-ypos_for_xaxis+ + (float)si/4.0 + }) + ); + + + //Draw the Y-axis + if ((diagram_data["yminvalue"]<=LITET)&& + (diagram_data["ymaxvalue"]>=-LITET)) + graph-> + polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + xpos_for_yaxis, + diagram_data["ysize"]-diagram_data["linewidth"], + xpos_for_yaxis, + si+labely + }), + 1, 1)[0]); + else + if (diagram_data["ymaxvalue"]<-LITET) + { + graph-> + polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + xpos_for_yaxis, + diagram_data["ysize"]-diagram_data["linewidth"], + + xpos_for_yaxis, + diagram_data["ysize"]-ypos_for_xaxis+si*4.0/3.0, + + xpos_for_yaxis-si/2.0, + diagram_data["ysize"]-ypos_for_xaxis+si, + + xpos_for_yaxis+si/2.0, + diagram_data["ysize"]-ypos_for_xaxis+si/1.5, + + xpos_for_yaxis, + diagram_data["ysize"]-ypos_for_xaxis+si/3.0, + + xpos_for_yaxis, + si+labely + }), + 1, 1)[0]); + } + else + if (diagram_data["yminvalue"]>LITET) + { + graph-> + polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + xpos_for_yaxis, + diagram_data["ysize"]-diagram_data["linewidth"], + + xpos_for_yaxis, + diagram_data["ysize"]-ypos_for_xaxis-si/3.0, + + xpos_for_yaxis-si/2.0, + diagram_data["ysize"]-ypos_for_xaxis-si/1.5, + + xpos_for_yaxis+si/2.0, + diagram_data["ysize"]-ypos_for_xaxis-si, + + xpos_for_yaxis, + diagram_data["ysize"]-ypos_for_xaxis-si*4.0/3.0, + + xpos_for_yaxis, + si+labely + }), + 1, 1)[0]); + } + + //Draw arrow + graph-> + polygone( + ({ + xpos_for_yaxis- + (float)si/4.0, + diagram_data["linewidth"]/2.0+ + (float)si+ + labely, + + xpos_for_yaxis, + diagram_data["linewidth"]/2.0+ + labely, + + xpos_for_yaxis+ + (float)si/4.0, + diagram_data["linewidth"]/2.0+ + (float)si+ + labely + })); + + //Calculate some shit + float xstart=(float)diagram_data["xstart"]; + float xmore=(-xstart+diagram_data["xstop"])/ + (diagram_data["xmaxvalue"]-diagram_data["xminvalue"]); + float ystart=(float)diagram_data["ystart"]; + float ymore=(-ystart+diagram_data["ystop"])/ + (diagram_data["ymaxvalue"]-diagram_data["yminvalue"]); + + draw_grid(diagram_data, xpos_for_yaxis, ypos_for_xaxis, + xmore, ymore, xstart, ystart, (float) si); + + //Paste the texts on the X-axis + int s=sizeof(diagram_data["xnamesimg"]); + for(int i=0; i<s; i++) + if ((diagram_data["values_for_xnames"][i]<diagram_data["xmaxvalue"])&& + (diagram_data["values_for_xnames"][i]>diagram_data["xminvalue"])) + { + graph->setcolor(@diagram_data["textcolor"]); + graph->paste_alpha_color(diagram_data["xnamesimg"][i], + @(diagram_data["textcolor"]), + (int)floor((diagram_data["values_for_xnames"][i] + - diagram_data["xminvalue"]) + * xmore+xstart + - diagram_data["xnamesimg"][i] + -> xsize()/2), + (int)floor(diagram_data["ysize"]-ypos_for_xaxis + + si/2.0)); + graph->setcolor(@diagram_data["axcolor"]); + graph-> + polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + ((diagram_data["values_for_xnames"][i] + - diagram_data["xminvalue"]) * xmore+xstart), + diagram_data["ysize"]-ypos_for_xaxis+si/4, + ((diagram_data["values_for_xnames"][i]- + diagram_data["xminvalue"]) + *xmore+xstart), + diagram_data["ysize"]-ypos_for_xaxis-si/4 + }), + 1, 1)[0]); + } + + //Paste the tests on the Y-axis + s=sizeof(diagram_data["ynamesimg"]); + for(int i=0; i<s; i++) + if ((diagram_data["values_for_ynames"][i]<diagram_data["ymaxvalue"])&& + (diagram_data["values_for_ynames"][i]>diagram_data["yminvalue"])) + { + graph->setcolor(@diagram_data["textcolor"]); + graph->paste_alpha_color( + diagram_data["ynamesimg"][i], + @(diagram_data["textcolor"]), + (int)floor(xpos_for_yaxis- + si/4.0-diagram_data["linewidth"]- + diagram_data["ynamesimg"][i]->xsize()), + (int)floor(-(diagram_data["values_for_ynames"][i]- + diagram_data["yminvalue"]) + *ymore+diagram_data["ysize"]-ystart + - diagram_data["ymaxynames"]/2)); + graph->setcolor(@diagram_data["axcolor"]); + graph-> + polygone(make_polygon_from_line( + diagram_data["linewidth"], + ({ + xpos_for_yaxis - si/4, + (-(diagram_data["values_for_ynames"][i] + - diagram_data["yminvalue"]) + *ymore+diagram_data["ysize"]-ystart), + + xpos_for_yaxis+ + si/4, + (-(diagram_data["values_for_ynames"][i]- + diagram_data["yminvalue"]) + *ymore+diagram_data["ysize"]-ystart) + }), + 1, 1)[0]); + } + + + //Paste labels ({xquantity, yquantity, xunit, yunit}) + if (diagram_data["labelsize"]) + { + graph->paste_alpha_color(labelimg, + @(diagram_data["labelcolor"]), + diagram_data["xsize"]-labelx + - (int)ceil((float)diagram_data["linewidth"]), + diagram_data["ysize"] + - (int)ceil((float)(ypos_for_xaxis-si/2))); + + string label; + int x; + int y; + GETFONT(yaxisfont); + if (diagram_data["labels"][3] && sizeof(diagram_data["labels"][3])) + label=diagram_data["labels"][1]+" ["+diagram_data["labels"][3]+"]"; //Ystorhet + else + label=diagram_data["labels"][1]; + if ((label!="")&&(label!=0)) + labelimg=notext->write(UNICODE(label,diagram_data["encoding"])) + -> scale(0,diagram_data["labelsize"]); + else + labelimg=image(diagram_data["labelsize"],diagram_data["labelsize"]); + + if (labelimg->xsize()<1) + labelimg=image(diagram_data["labelsize"],diagram_data["labelsize"]); + + if (labelimg->xsize()> + diagram_data["xsize"]) + labelimg=labelimg->scale(diagram_data["xsize"], 0); + + + x=max(2,((int)floor((float)xpos_for_yaxis)-labelimg->xsize()/2)); + x=min(x, graph->xsize()-labelimg->xsize()); + + y=0; + + if (label && sizeof(label)) + graph->paste_alpha_color(labelimg, + @(diagram_data["labelcolor"]), x, + 2+labely-labelimg->ysize()); + + } + + //Draw the data + int farg=0; + + foreach(diagram_data["data"], array(float) d) + { + d-=({VOIDSYMBOL}); + for(int i=0; i<sizeof(d)-1; i++) + { + d[i]=(d[i]-diagram_data["xminvalue"])*xmore+xstart; + i++; + d[i]=-(d[i]-diagram_data["yminvalue"])*ymore+diagram_data["ysize"] + - ystart; + } + + graph->setcolor(@(diagram_data["datacolors"][farg++])); + + draw(graph, diagram_data["linewidth"],d); + } + + diagram_data["ysize"]-=diagram_data["legend_size"]; + diagram_data["image"]=graph; + return diagram_data; +} diff --git a/lib/modules/Graphics.pmod/Graph.pmod/create_pie.pike b/lib/modules/Graphics.pmod/Graph.pmod/create_pie.pike new file mode 100644 index 0000000000000000000000000000000000000000..bd7d191b0621a9722999f39c0cfae9107b62172d --- /dev/null +++ b/lib/modules/Graphics.pmod/Graph.pmod/create_pie.pike @@ -0,0 +1,502 @@ +#!NOMODULE + +#include "graph.h" + +// import Image; +import Array; +import Stdio; + +inherit "polyline.pike"; +inherit "create_graph.pike"; +inherit "create_bars.pike"; + +constant cvs_version = "$Id: create_pie.pike,v 1.1 1999/09/30 13:04:05 hedda Exp $"; + +/* + * name = "BG: Create pies"; + * doc = "Business Graphics sub-module for drawing pie-charts."; + */ + +/* +These functions were written by Henrik "Hedda" Wallin (hedda@idonex.se) +Create_pie can draw pie charts in different forms. + +The data is taken from the diagram_data-mapping which is described in + doc/diagram_internals.txt + + +*/ + + +mapping(string:mixed) create_pie(mapping(string:mixed) diagram_data) +{ + //Only tested with xsize>=100 + int si=diagram_data["fontsize"]; + + string where_is_ax; + + object(Image.image) piediagram; + + init_bg(diagram_data); + piediagram=diagram_data["image"]; + setinitcolors(diagram_data); + + set_legend_size(diagram_data); + + diagram_data["ysize"]-=diagram_data["legend_size"]; + + //Do the standard init (The init function is in create_graph) + init(diagram_data); + + //Initiate values + int|void size=diagram_data["xsize"]; + array(int|float) numbers=diagram_data["data"][0]; + void | array(string) names=diagram_data["xnames"]; + void|int twoD=diagram_data["drawtype"]=="2D"; + void|array(array(int)) colors=diagram_data["datacolors"]; + array(int)bg=diagram_data["bgcolor"]; + array(int)fg=diagram_data["textcolor"]; + int tone=diagram_data["tone"]; + + for(int i; i<sizeof(numbers); i++) + if ((float)numbers[i]<0.0) + numbers[i]*=-1.0; + + + + + ////////////////////// + + + + + object* text; + object notext; + int ymaxtext; + int xmaxtext; + + int imysize; + int imxsize; + float *arr=allocate(802); + float *arr2=allocate(802); + float *arr3=allocate(802); + float *arrplus=allocate(802); + float *arrpp=allocate(802); + + int yc; + int xc; + int xr; + int yr; + + mixed sum; + int sum2; + + int* pnumbers=allocate(sizeof(numbers)); + int* order=indices(numbers); + + int edge_nr=0; + + + + //create the text objects + if (names) + text=allocate(sizeof(names)); + + if (diagram_data["3Ddepth"]>diagram_data["ysize"]/5) + diagram_data["3Ddepth"]=diagram_data["ysize"]/5; + + GETFONT(xnamesfont); + if (names) + if (notext) + for(int i=0; i<sizeof(names); i++) + { + if ((names[i]!=0) && (names[i]!="")) + text[i]=notext + ->write(UNICODE((string)(names[i]),diagram_data["encoding"])) + ->scale(0,diagram_data["fontsize"]); + else + text[i]=Image.image(diagram_data["fontsize"], + diagram_data["fontsize"]); + + if (text[i]->xsize()<1) + text[i]=Image.image(diagram_data["fontsize"], + diagram_data["fontsize"]); + + if (text[i]->xsize()>diagram_data["xsize"]/5+diagram_data["3Ddepth"]) + text[i]=text[i]->scale((int)diagram_data["xsize"]/5, 0); + + if (text[i]->ysize()>diagram_data["ysize"]/5-diagram_data["3Ddepth"]) + text[i]=text[i]->scale(0, (int)diagram_data["ysize"]/5- + diagram_data["3Ddepth"]); + + if (xmaxtext<(text[i]->xsize())) + xmaxtext=text[i]->xsize(); + if (ymaxtext<(text[i]->ysize())) + ymaxtext=text[i]->ysize(); + + } + else + throw(({"Missing font or similar error!\n", backtrace() })); + + int nameheight=write_name(diagram_data); + + //Some calculations + if (twoD) + { + xc=diagram_data["xsize"]/2; + yc=diagram_data["ysize"]/2+nameheight/2; + xr=(int)min(xc-xmaxtext-ymaxtext-1-diagram_data["linewidth"], + yc-2*ymaxtext- + 1-diagram_data["linewidth"]-nameheight); + yr=xr; + } + else + { + xc=diagram_data["xsize"]/2; + yc=diagram_data["ysize"]/2-diagram_data["3Ddepth"]/2+nameheight/2; + yr=(int)(min(xc-xmaxtext-ymaxtext-1-diagram_data["3Ddepth"]/2, + yc-2*ymaxtext-1-nameheight) + -diagram_data["linewidth"]); + xr=(int)(min(xc-xmaxtext-ymaxtext-1, + yc+diagram_data["3Ddepth"]/2- + 2*ymaxtext-1-nameheight)- + diagram_data["linewidth"]); + } + float w=diagram_data["linewidth"]; + + if (xr<2) + throw(({"Image to small for this pie-diagram.\n" + "Try smaller font or bigger image!\n", backtrace() })); + + //initiate the 0.25*% for different numbers: + //Ex: If numbers is ({3,3}) pnumbers will be ({200, 200}) + sum=`+(@ numbers); + int i; + + if (sum>LITET) + { + for(int i=0; i<sizeof(numbers); i++) + { + float t=(float)(numbers[i]*400)/sum; + pnumbers[i]=(int)floor(t); + numbers[i]=t-floor(t); + } + + + //Now the rests are in the numbers-array + //We now make sure that the sum of pnumbers is 400. + sort(numbers, order); + sum2=`+(@ pnumbers); + i=sizeof(pnumbers); + while(sum2<400) + { + pnumbers[order[--i]]++; + sum2++; + } + } + else + if (sizeof(numbers)>1) + { + for(int i=0; i<sizeof(numbers); i++) + pnumbers[i]=(int)floor(400.0/(float)sizeof(numbers)); + int j=400-`+(@pnumbers); + for(int i=0; i<j; i++) + pnumbers[i]++; + } + else + pnumbers=({400}); + + //Initiate the piediagram! + float FI=0; + if (diagram_data["center"]) + { + //If to great center integer is given, module is used. + // Center should not be greater than sizeof(data[0]). + diagram_data["center"]%=(1+sizeof(numbers)); + FI=(400-`+(0,@pnumbers[0..diagram_data["center"]-2]) + -pnumbers[diagram_data["center"]-1]*0.5)*2.0*PI/400.0; + } + else + if (diagram_data["rotate"]) + FI=((float)(diagram_data["rotate"])*2.0*PI/360.0)%(2*PI); + float most_down=yc+yr+w; + float most_right=xc+xr+w; + float most_left=xc-xr-w; + + for(int i=0; i<401; i++) + { + arr[2*i]=xc+xr*sin((i*2.0*PI/400.0)+FI); + arr[1+2*i]=yc+yr*sin(-PI/2+i*2.0*PI/400.0+FI); + arr2[2*i]=xc+(xr+w)*sin((i*2.0*PI/400.0)); + arr2[2*i+1]=yc+(w+yr)*sin(-PI/2+i*2.0*PI/400.0); + arr3[2*i]=xc+(xr+w)*sin((-i*2.0*PI/400.0+FI)); + arr3[2*i+1]=yc+(w+yr)*sin(-PI/2-i*2.0*PI/400.0+FI); + } + + //Draw the slices + if (sizeof(diagram_data["datacolors"])> + sizeof(diagram_data["data"][0])) + diagram_data["datacolors"]=diagram_data["datacolors"] + [0..sizeof(diagram_data["data"][0])-1]; + + int t=sizeof(diagram_data["datacolors"]); + + float miniwxr; + float miniwyr; + + if (!twoD) + { + array arrfoo=copy_value(arr2); + miniwxr=max((w+xr)/2,(w+xr)-diagram_data["3Ddepth"]/10); + miniwyr=max((w+yr)/2,(w+yr)-diagram_data["3Ddepth"]/10); + for(int i=200; i<604; i+=2) + { + arrfoo[i]=xc+miniwxr*sin((i*PI/400.0)); + arrfoo[i+1]=yc+miniwyr*sin(-PI/2+i*PI/400.0)+ + diagram_data["3Ddepth"]; + } + //arrfoo[i]=arr2[i]+diagram_data["3Ddepth"]; + for(int i=0; i<401; i++) + { + arrplus[2*i]=xc+(xr+w)*sin((i*2.0*PI/400.0)+FI); + arrplus[1+2*i]=yc+(w+yr)*sin(-PI/2+i*2.0*PI/400.0+FI); + arrpp[2*i]=xc+miniwxr*sin((-i*2.0*PI/400.0)+FI); + arrpp[1+2*i]=yc+miniwyr*sin(-PI/2-i*2.0*PI/400.0+FI)+ + diagram_data["3Ddepth"]; + } + object skugg; + skugg=Image.image(piediagram->xsize(),piediagram->ysize(), 255,255,255); + object foo; + foo=Image.image(piediagram->xsize(),piediagram->ysize(), 255,255,255); + skugg->tuned_box(xc,yc-yr-1,xc+xr+1,1+yc+yr+diagram_data["3Ddepth"], + ({ + ({255,255,255}), + ({100,100,100}), + ({255,255,255}), + ({100,100,100}) + })); + skugg->tuned_box(xc-xr-1,yc-yr-1,xc,1+yc+yr+diagram_data["3Ddepth"], + ({ + ({100,100,100}), + ({255,255,255}), + ({100,100,100}), + ({255,255,255}) + })); + skugg->polyfill(arr2[600..801]+({xc,0,0,0})); + skugg->polyfill( + arr2[..201] + + + ({piediagram->xsize()-1,0,xc,0}) + ); + skugg->polyfill( + arr2[200..201]+ + arrfoo[200..401] + + + ({xc,piediagram->ysize()-1, + piediagram->xsize()-1,piediagram->ysize()-1}) + ); + skugg->polyfill( + arrfoo[400..601]+ + arr2[600..601]+ + ({0,piediagram->ysize()-1,xc,piediagram->ysize()-1 + }) + ); + + edge_nr=0; + for(i=0; i<t; i++) + { + piediagram->setcolor( + @diagram_data["datacolors"][i] + ); + + if (pnumbers[i]) + { + piediagram-> + polygone( + (arrplus[2*edge_nr..2*(edge_nr+pnumbers[i]+2)+1] + + + arrpp[800-2*(edge_nr+pnumbers[i]+2).. + 801-2*edge_nr]) + ); + } + edge_nr+=pnumbers[i]; + } + piediagram=piediagram*skugg; + + piediagram->setcolor(0,0,0); + piediagram->polyfill( + make_polygon_from_line(diagram_data["linewidth"], + ({ + xc+(xr+w/2.0), + yc})+ + arrfoo[200.. + 601]+ + ({xc-(xr+w/2.0), + yc}), + 0,1)[0]); + } + + edge_nr=0; + for(i=0; i<t; i++) + { + piediagram=piediagram->setcolor(@diagram_data["datacolors"][i]); + if (pnumbers[i]) + piediagram=piediagram->polyfill(({(float)xc,(float)yc})+ + arr[2*edge_nr..2*(edge_nr+pnumbers[i]+2)+1]); + edge_nr+=pnumbers[i]; + } + + + + edge_nr=pnumbers[0]; + + //black borders + if (diagram_data["linewidth"]>LITET) + { + piediagram->setcolor(@diagram_data["axcolor"]); + piediagram->polygone( + make_polygon_from_line(diagram_data["linewidth"], + ({ + xc, + yc, + arr[0], + arr[1] + }) + , + 0, 1)[0] + ); + + for(int i=1; i<sizeof(pnumbers); i++) + { + piediagram-> + polygone( + make_polygon_from_line(diagram_data["linewidth"], + ({xc + ,yc, + arr[2*(edge_nr)], + arr[2*(edge_nr)+1] + }) + , + 0, 1)[0] + ); + + edge_nr+=pnumbers[i]; + } + piediagram->polygone(arr[..401] + +arr3[400..]); + piediagram->polygone(arr[400..] + +arr3[..401]); + } + + piediagram->setcolor(255,255,255); + + //And now some shading! + if (!twoD) + { + object below; + int *b=({70,70,70}); + int *a=({0,0,0}); + + + object tbild; + + int imxsize=piediagram->xsize(); //diagram_data["xsize"]; + int imysize=piediagram->ysize(); //diagram_data["ysize"]+diagram_data["legendsize"]; + + if(tone) + { + + + tbild=Image.image(imxsize, imysize, 255, 255, 255)-> + tuned_box(0, 0 , 1, imysize, + ({a,a,b,b})); + tbild=tbild->paste(tbild->copy(0,0,0, imysize), 1, 0); + tbild=tbild->paste(tbild->copy(0,0,1, imysize), 2, 0); + tbild=tbild->paste(tbild->copy(0,0,3, imysize), 4, 0); + tbild=tbild->paste(tbild->copy(0,0,7, imysize), 8, 0); + tbild=tbild->paste(tbild->copy(0,0,15, imysize), 16, 0); + if (imxsize>32) + tbild=tbild->paste(tbild->copy(0,0,31, imysize), 32, 0); + + if (imxsize>64) + tbild-> + paste(tbild->copy(0,0,63, imysize), 64, 0); + if (imxsize>128) + tbild=tbild->paste(tbild->copy(0,0,128, imysize), 128, 0); + if (imxsize>256) + tbild=tbild->paste(tbild->copy(0,0,256, imysize), 256, 0); + if (imxsize>512) + tbild=tbild->paste(tbild->copy(0,0,512, imysize), 512, 0); + piediagram+=tbild; + } + + //Vertical lines below + edge_nr=(int)(FI*200.0/PI+0.5); + piediagram->setcolor(0,0,0); + for(int i=0; i<sizeof(pnumbers); i++) + { + if (sin(-PI/2+edge_nr*2.0*PI/400.0)>0) + { + + float x1=xc+(xr+w/2.0)*sin((edge_nr*2.0*PI/400.0)); + float y1=yc+(w/2.0+yr)*sin(-PI/2+edge_nr*2.0*PI/400.0); + float x2=xc+(miniwxr-w/2.0)*sin((edge_nr*2.0*PI/400.0)); + float y2=yc+diagram_data["3Ddepth"] + +(miniwyr-w/2.0)*sin(-PI/2+edge_nr*2.0*PI/400.0); + piediagram=piediagram-> + polygone( + make_polygon_from_line(diagram_data["linewidth"], + ({ + x1,y1, + x2,y2 + }) + , + 0, 1)[0] + ); + + } + edge_nr+=pnumbers[i]; + } + + } + + + //write the text! + int|float place; + sum=0; + if (names) + for(int i=0; i<min(sizeof(pnumbers), sizeof(text)); i++) + { + int t; + sum+=pnumbers[i]; + place=sum-pnumbers[i]/2; + if (FI) + { + place=place+FI*400.0/(2.0*PI); + if (place>400) + place=place%400; + } + piediagram=piediagram->setcolor(255,0, 0); + + + t=(place<202)?0:-text[i]->xsize(); + int yt=0; + if (((place>100)&&(place<300))&& + (!twoD)) + yt=diagram_data["3Ddepth"]; + + int ex=text[i]->ysize(); + int x=(int)(xc +t+ (xr+ex)*sin(place*PI*2.0/400.0+0.0001)); + int y=(int)(-text[i]->ysize()/2+yc +yt+ + (yr+ex)*sin(-PI/2.0+place*PI*2.0/400.0+0.0001)); + + piediagram=piediagram->paste_alpha_color(text[i], @fg, x, y); + } + + diagram_data["ysize"]-=diagram_data["legend_size"]; + diagram_data["image"]=piediagram; + return diagram_data; + + + +} diff --git a/lib/modules/Graphics.pmod/Graph.pmod/doc.txt b/lib/modules/Graphics.pmod/Graph.pmod/doc.txt new file mode 100644 index 0000000000000000000000000000000000000000..c71521ea938832717ab66ddecf0e6c90f56ebc34 --- /dev/null +++ b/lib/modules/Graphics.pmod/Graph.pmod/doc.txt @@ -0,0 +1,299 @@ +Graph-doc + +Methods +Image.Image Graphics.Graph.pie(mapping(string:mixed) graphdata) +Image.Image Graphics.Graph.bars(mapping(string:mixed) graphdata) +Image.Image Graphics.Graph.sumbars(mapping(string:mixed) graphdata) +Image.Image Graphics.Graph.line(mapping(string:mixed) graphdata) +Image.Image Graphics.Graph.norm(mapping(string:mixed) graphdata) +Image.Image Graphics.Graph.graph(mapping(string:mixed) graphdata) + +The mapping graphdata can contain the following (all arguments can be +left out): + +Note: type and subtype is left out. + +drawtype Only "linear" works for now. +tone If present a Pie-chart will be toned. +3Ddepth How much 3D-depth a graph will have in pixels Default is 10. +data An array of arrays. Each array describing a data-set. + The graph-function however should be fed with an array + of arrays with X,Y-pairs. +labels An array(string) with four elements + ({xquantity, yquantity, xunit, yunit}). The strings will + be written on the axis. +xnames An array(string) with the text that will be written under + the X-axis. This should be the same size as sizeof(data). +ynames An array(string) with the text that will be written to + the left of the Y-axis. +fontsize The size of the text. Default is 10. +labelsize The size of the text for labels. Default is fontsize. +legendfontsize The size of the text for the legend. Default is fontsize. +legend_texts The texts that will be written the legend. +values_for_xnames An array(float) that describes where the ynames should + be placed. The numbers are like the data-numbers. + Default is equally distributed. +values_for_ynames An array(float) that describes where the ynames should + be placed. The numbers are like the data-numbers. + Default is equally distributed. +xsize X-size of the graph in pixels. Default is 100. +ysize Y-size of the graph in pixels. Default is 100. +image An image that the graph will be drawn on. +legendcolor The color of the text in the legend. Default is? +legendimage I have no idea. +bgcolor The bakground-color. If the the background is a image + this color is used for antialias the texts. +gbimage Some sort of image... +axcolor The color of the axis. +datacolors An array of colors for the datasets. +backdatacolors An array of color that do something... +textcolor Color of the text. Defualt is black. +labelcolor Color of the labeltexts. +orient Can be "hor" or "vert". +linewidth Width of the lines. +backlinewidth Width of the outline-lines. Defualt is 0. +vertgrid If the vertical grid should be present. +horgrid If the horizontal grid should be present. +gridwidth Width of the grid. Defualt is linewidth/4. +rotate How much a the Pie in a Pie-shart should be rotated in degrees. +center Makes the first Pie-slice be centered. +bw Draws the grapf black and white. +eng Writes the numbers in eng format. +neng Writes the numbers in engformat except for 0.1 < x < 1.0 +xmin Where the X-axis should start. This will be overrided + by datavalues. +ymin Where the Y-axis should start. This will be overrided + by datavalues. +name A string with the name of the graph that will be written + at top of the graph. +namecolor The color of the name. +font The font that will be used. Default is Image.Font(). +gridcolor The color of the grid. Default is black. + + + + + +Diagram info: + +Huvud-funktioner: +create_graph(mapping diagram_data); +create_bars(mapping diagram_data); +create_sumbars(mapping diagram_data); +create_pie(mapping diagram_data); + + +Graphs: +Graphs är punkter eller punkter med linjer mellan sig. Punkterna anges +med x, y. +Indata på formen x, y, y, y omvandlas av inläsningsfunktionen. + +Bars: +Barsdata består av en lista av listor med värden i. Värdena skrivs +ut som staplar eller negativa staplar. + +Data lagras i ett data-mapping: +diagram_data + + +string type; +//Är lika med "graph", "bars", "sumbars", "pie"; + +string subtype; +//Är alltid "line" för graph-er. Tänkbara för bars är "line", "dot", "box" +//Är "pie" för tårtor? +//Sumbars: "norm" ger alla staplar lika höga. + +//Följande gäller för bars-line och graph-diagram +string drawtype; + "level"; //Om !=0 så är bars-diagrammet bara vågräta streck + "linear"; //Om !=0 så är bars/graph-diagrammet linjärt spline (default) + "quadratic"; //Om !=0 så är bars/graph-diagrammet kvadratiskt spline + "cubic";//Om !=0 så är bars/graph-diagrammet kubiskt spline + //För bars-box och pie: + "2D" - ritar 2D boxar/kakor + "3D" - flash!!! + +int tone; + //Anger om skuggning ska ske + +float|int 3Ddepth; +//Anger hur djup 3D ska vara i pixel + +array(array(float|string)) data; +//Innehåller för graph + // en array med arrayer av typ x-pos1, y-pos1, x-pos2, ... +//För Bars: + // x-data1, x-data2, ... +//För pie: + // value1, value2, ... +//Om ett dataelement är lika med "\n" så betyder det VOID + + +array(string) labels; +//Innehåller en array med ({xstorhet, ystorhet, xenhet, yenhet}) +// För bars kan används inte xenhet +//Om ett dataelement är lika med "\n" så betyder det VOiD + +array(string) xnames; +//Innehåller en array med olika namn som ska stå längs x-axeln +//(t ex jan, feb). +//Om ett dataelement är lika med "\n" så betyder det VOiD + + +array(string) ynames; +//Innehåller en array med olika namn som ska stå längs y-axeln (t ex +//lite, mycket). +// Dessa namn skrivs i diagrammet av create_pie. +//Om ett dataelement är lika med "\n" så betyder det VOiD + + +array(object(image)) xnamesimg; +array(object(image)) ynamesimg; +//bilderna på x- och ynames. + +int xmaxxnames; +int ymaxxnames; +int xmaxynames; +int ymaxynames; +//Anger max-storleken på text-bilderna. + +int fontsize; +//anger med vilken storlek texten ska skrivas + +int labelsize; +// anger med vilken storlek texten för labels ska skrivas +// Denna ska sättas till 0 om det inte finns några labels!!! + +int legendfontsize; +//Anger storleken på legend-texten. +// (Default fontsize) +//Om den är för stor minskas den. + +int legend_size; +//Storleken på legenden. + +array(string) legend_texts; +// innehåller texten som ska stå i Legenden. +// Det ska vara en text per data +// Är 0 om ingen text ska skrivas ut. +array(froat) values_for_xnames; +//Innehåller en array med nivåerna som ska förknippas med vissa xnames. +//Om dessa inte anges kan de sättas ut jämt. +//Bars sätter denna själv +// Dessa namn är de som används av pie-diagramet för legenden. +//Om ett dataelement är lika med "\n" så betyder det VOID + + +array(froat) values_for_ynames; +//Innehåller en array med nivåerna som ska förknippas med vissa ynames. +//Om dessa inte anges kan de sättas ut jämt. + +float xmaxvalue, xminvalue, ymaxvalue, yminvalue; +//anger inom vilka områden man ska förvänta sig data. Minvalue sätts +//i normala fall till min(0, minsta_värde) och maxvalue till +//max(0, största_värde). + + +float xspace, float yspace, +//anger hur lång det ska vara mellan utskrifterna av nuffror/xnames/ynames + +int xsize, ysize; +//anger hur stor bild man ska generera. + +"image":image-object, +// Denna kan sättas till bakgrundsbilden innan create_XXX anropas. +// OBS om den ska användas så måste bgcolor vara 0!!! + +"xstart": var_i_bilden_vi_kan_börja_rita_data-int, //från höger +"ystart": var_i_bilden_vi_kan_börja_rita_data-int, //Nerifrån! +"xstop":int, //från höger +"ystop":int //Nerifrån! + +"legendcolor":({int r, int g, int b}) +//bgfärgen på bakgrunden i legenden +//Om legendcolor är 0 så är det bilden: + +"legendimage": object(image) +// som ska användas som bakgrundsbild. + +"bgcolor":({int r, int g, int b}) +//färgen på bakgrunden i bilden +//Om bgcolor är 0 så är det bilden: + +"bgimage": object(image) +// som ska användas som bakgrundsbild. + +"axcolor":({int r, int g, int b}) +//Färgen på axlarna och på outlines i pie + +"datacolors":({({int r, int g, int b}), ... }) +//Färgen på linjerna/barsen/slicerna +//För grapher och bars måste de vara av samma antal som antalet +//uppsättningar data + +"backdatacolors":({({int r, int g, int b}), ... }) +//Färgen på kontueren kring linjerna/barsen/slicerna +//För grapher och bars måste de vara av samma antal som antalet +//uppsättningar data + +"textcolor":({int r, int g, int b}) +// färgen på texten + +"labelcolor":({int r, int g, int b}) +// färgen på labelstexten (default är textcolor):({int r, int g, int b}) + +"orient":"hor" eller "vert" +//Anger om skriften på x-axel ska skrivas horizontelt eller vertikalt. +//hor är default. + +"linewidth":float bredd på alla linjer + +"backlinewidth":float bredd på alla linjer + +"grindwidth":float bredd på alla linjer i grinden + +"vertgrind": int om denna är så blir det vertikala grind + +"horgrind": int om denna är så blir det horizontella grind + +"image":object(image) +//bilden som grafen ritas i. + +"rotate": int|float +//How much the pie should be rotated in degres (0-360). + +"center": int +//If center==0 no centration takes place. +//Else the slice number center-1 will be in the topp. + +"datasize": int +//how many datas there are in the biggest dataset + +"bw": int +//If this is != 0 the graphs are drawn in black and white. +//The black and white colors are set to be as different as possible +//If colors are given, bw is ignored +//the bgpicture or bg is not affected. + +"eng": mixed +//If this is != 0 the information on the axis are presented in +//engineer-format (120k, 34u (LPC?)) + +"neng": mixed +//If this is != 0 the information on the axis are presented in +//Norings engineer-format (120k, 34u, and 0.434) + +"xmin": int +//If this is !=0 the xaxis starts at the minumum value. +"ymin": int +//If this is !=0 the yaxis starts at the minumum value. + +"name":string +//The name of the diagram that will be written at the top of the diagram + +"namesize":int +//The size of the name. If it is 0, "fontsize" is used instead. + +"namecolor":int +//The color of the name. If it is 0, "textcolor" is used instead. diff --git a/lib/modules/Graphics.pmod/Graph.pmod/graph.h b/lib/modules/Graphics.pmod/Graph.pmod/graph.h new file mode 100644 index 0000000000000000000000000000000000000000..2876beb03151f8f16cd2e48af9eff1caa8814448 --- /dev/null +++ b/lib/modules/Graphics.pmod/Graph.pmod/graph.h @@ -0,0 +1,30 @@ +/* + * name = "BG: diagram.h"; + * doc = "Business Graphics common things. You must upgrade this component to use newer versions of BG."; + * + * string cvs_version="$Id: graph.h,v 1.1 1999/09/30 13:04:11 hedda Exp $"; + */ + + +#define max(i, j) (((i)>(j)) ? (i) : (j)) +#define min(i, j) (((i)<(j)) ? (i) : (j)) +// #define abs(arg) ((arg)*(1-2*((arg)<0))) // Don't do it this way! /grubba +#define abs(arg) (((arg)<0) ? -(arg) : (arg)) + +#define PI 3.14159265358979 +#define VOIDSYMBOL "\n" +#define SEP "\t" +#define UNICODE(TEXT,ENCODING) Locale.Charset.decoder(ENCODING)->feed(TEXT)->drain() + +private constant LITET = 1.0e-38; +private constant STORTLITET = 1.0e-30; +private constant STORT = 1.0e30; + + +//This is used in Roxen. Don't work with only Pike +//#define GETFONT(WHATFONT) object notext=resolve_font(diagram_data->WHATFONT||diagram_data->font); +#define GETFONT(WHATFONT) object notext=diagram_data->font; + +//#define BG_DEBUG 1 +#define error(X) throw( ({ (X), backtrace()[0..sizeof(backtrace())-2] }) ) + diff --git a/lib/modules/Graphics.pmod/Graph.pmod/module.pmod b/lib/modules/Graphics.pmod/Graph.pmod/module.pmod new file mode 100644 index 0000000000000000000000000000000000000000..d2cc297d6e41b1b50f723b64ed936ede8f9302b1 --- /dev/null +++ b/lib/modules/Graphics.pmod/Graph.pmod/module.pmod @@ -0,0 +1,147 @@ + +#include "graph.h" + +import Array; +import Stdio; + +private inherit "polyline.pike"; +private inherit "create_graph.pike"; +private inherit "create_bars.pike"; +private inherit "create_pie.pike"; +private inherit "create_graph.pike"; + +//This function sets all unset objects in diagram_data and +//checks some other stuff. +mapping(string:mixed) check_mapping(mapping(string:mixed) diagram_data, + string type) +{ + mapping m=diagram_data; + + + //This maps from mapping entry to defaulvalue + //This may be optimized, but I don't think the zeros mather. + //I just wanted all indices to be here. But this will not be + //Updated so it is a bad idea in the long run. + mapping md= + ([ + "type":1, //This is already checked + "subtype":1, //This is already checked + "drawtype":"linear", //Will be set to "2D" for pie below + "tone":0, + "3Ddepth":10, + "data":({({1.0}), ({2.0}), + ({4.0})}), //Will be set to something else with graph + "labels":0, //array(string) ({xquantity, yquantity, xunit, yunit}) + "xnames":0, //array(string) ? + "ynames":0, //array(string) ? + "fontsize":10, + "labelsize":0, //Default is set somewhere else + "legendfontsize":0, + "legend_texts":0, + "values_for_xnames":0, + "values_for_ynames":0, + //xmaxvalue, xminvalue, ymaxvalue, yminvalue; + "xsize":100, + "ysize":100, + "image":0, + "legendcolor":0, + "legendimage":0, + "bgcolor":0, + "gbimage":0, + "axcolor":({0,0,0}), + "datacolors":0, + "backdatacolors":0, + "textcolor":({0,0,0}), + "labelcolor":0, + "orient":"hor", + "linewidth":0, + "backlinewidth":0, + "vertgrid":0, + "horgrid":0, + "gridwidth":0, + "rotate":0, + "center":0, + "bw":0, + "eng":0, + "neng":0, + "xmin":0, + "ymin":0, + "name":0, + "namecolor":0, + "font":Image.Font(), + "gridcolor":({0,0,0}) + ]); + foreach(indices(md), string i) + if (!m[i]) m[i]=md[i]; + + + switch(type) + { + case "pie": + m->type = "pie"; + m->subtype="pie"; + md->drawtype="2D"; + break; + case "bars": + m->type = "bars"; + m->subtype = "box"; + m_delete( m, "drawtype" ); + break; + case "line": + m->type = "bars"; + m->subtype = "line"; + break; + case "norm": + m->type = "sumbars"; + m->subtype = "norm"; + break; + case "graph": + m->type = "graph"; + m->subtype = "line"; + md->data=({({1.0,1.0}),({2.0,1.0}),({2.0,2.0}),({1.5,1.5})}); + break; + case "sumbars": + m->type = "sumbars"; + break; + default: + error("This error will never happen error in Diagram.pmod.\n"); + } + + + return diagram_data; +} + +Image.Image pie(mapping(string:mixed) diagram_data) +{ + check_mapping(diagram_data, "pie"); + return create_pie(diagram_data)->image; +} + +Image.Image bars(mapping(string:mixed) diagram_data) +{ + check_mapping(diagram_data, "bars"); + return create_bars(diagram_data)->image; +} + +Image.Image sumbars(mapping(string:mixed) diagram_data) +{ + check_mapping(diagram_data, "sumbars"); + return create_bars(diagram_data)->image; +} +Image.Image line(mapping(string:mixed) diagram_data) +{ + check_mapping(diagram_data, "line"); + return create_bars(diagram_data)->image; +} + +Image.Image norm(mapping(string:mixed) diagram_data) +{ + check_mapping(diagram_data, "norm"); + return create_bars(diagram_data)->image; +} + +Image.Image graph(mapping(string:mixed) diagram_data) +{ + check_mapping(diagram_data, "graph"); + return create_graph(diagram_data)->image; +} diff --git a/lib/modules/Graphics.pmod/Graph.pmod/polyline.pike b/lib/modules/Graphics.pmod/Graph.pmod/polyline.pike new file mode 100644 index 0000000000000000000000000000000000000000..3033b772f459146ad733a3f77f9b47e4c58ed3d8 --- /dev/null +++ b/lib/modules/Graphics.pmod/Graph.pmod/polyline.pike @@ -0,0 +1,218 @@ +#!NOMODULE +/* + * name = "BG: Create pies"; + * doc = "Business Graphics sub-module providing draw functions."; + * $Id: polyline.pike,v 1.1 1999/09/30 13:04:17 hedda Exp $ + */ + +#define CAP_BUTT 0 +#define CAP_ROUND 1 +#define CAP_PROJECTING 2 + +#define JOIN_MITER 0 +#define JOIN_ROUND 1 +#define JOIN_BEVEL 2 + + +#define CAPSTEPS 10 +#define JOINSTEPS 5 + + +constant PI = 3.1415926535897932384626433832795080; + +/* + * Some optimizations for the cappings. + * + * /grubba (who got tired of BG beeing so slow) + */ + +static array(float) init_cap_sin_table() +{ + array(float) s_t = allocate(CAPSTEPS); + + for (int i = 0; i < CAPSTEPS; i++) { + s_t[i] = sin(PI*i/(CAPSTEPS-1)); + } + return(s_t); +} + +static array(float) cap_sin_table = init_cap_sin_table(); + +static array(float) init_cap_cos_table() +{ + array(float) c_t = allocate(CAPSTEPS); + + for (int i = 0; i < CAPSTEPS; i++) { + c_t[i] = cos(PI*i/(CAPSTEPS-1)); + } + return(c_t); +} + +static array(float) cap_cos_table = init_cap_cos_table(); + + + +static private array(float) xyreverse(array(float) a) +{ + array(float) r = reverse(a); + int n = sizeof(r)/2; + while(n--) { + float t = r[n<<1]; + r[n<<1] = r[(n<<1)+1]; + r[(n<<1)+1] = t; + } + return r; +} + +array(array(float)) make_polygon_from_line(float h, array(float) coords, + int|void cap_style, int|void join_style) +{ + int points = sizeof(coords)/2; + int closed = points>2 && + coords[0] == coords[(points-1)<<1] && + coords[1] == coords[((points-1)<<1)+1]; + if(closed) + --points; + int point; + float sx = h/2, sy = 0.0; + array(float) left = ({ }), right = ({ }); + + for(point=0; point<points; point++) { + + float ox = coords[point<<1], oy = coords[(point<<1)+1]; + int t = (point==points-1 ? (closed? 0 : point) : point+1); + float tx = coords[t<<1], ty = coords[(t<<1)+1]; + float dx = tx - ox, dy = ty - oy, dd = sqrt(dx*dx + dy*dy); + if(dd > 0.0) { + sx = (-dy*h) / (dd*2); + sy = (dx*h) / (dd*2); + } + + if(point == 0 && !closed) { + + /* Initial cap */ + + switch(cap_style) { + case CAP_BUTT: + left += ({ ox+sx, oy+sy }); + right += ({ ox-sx, oy-sy }); + break; + case CAP_PROJECTING: + left += ({ ox+sx-sy, oy+sy+sx }); + right += ({ ox-sx-sy, oy-sy+sx }); + break; + case CAP_ROUND: + array(float) initial_cap = allocate(CAPSTEPS*2); + + int j=0; + for(int i=0; i<CAPSTEPS; i++) { + initial_cap[j++] = ox + sx*cap_cos_table[i] - sy*cap_sin_table[i]; + initial_cap[j++] = oy + sy*cap_cos_table[i] + sx*cap_sin_table[i]; + } + right += initial_cap; + break; + } + + } + + if(closed || point<points-1) { + + /* Interconnecting segment and join */ + + if(point == points-2 && !closed) + /* Let the final cap generate the segment */ + continue; + + int t2 = (t==points-1 ? 0 : t+1); + float t2x = coords[t2<<1], t2y = coords[(t2<<1)+1]; + float d2x = t2x - tx, d2y = t2y - ty, d2d = sqrt(d2x*d2x + d2y*d2y); + float s2x, s2y; + if(d2d > 0.0) { + s2x = (-d2y*h) / (d2d*2); + s2y = (d2x*h) / (d2d*2); + } else { + s2x = sx; + s2y = sy; + } + + float mdiv = (sx*s2y-sy*s2x); + if(mdiv == 0.0) { + left += ({ tx+sx, ty+sy, tx+s2x, ty+s2y }); + right += ({ tx-sx, ty-sy, tx-s2x, ty-s2y }); + } else { + float m = (s2y*(sy-s2y)+s2x*(sx-s2x))/mdiv; + + /* Left join */ + + switch(mdiv<0.0 && join_style) { + case JOIN_MITER: + left += ({ tx+sx+sy*m, ty+sy-sx*m }); + break; + case JOIN_BEVEL: + left += ({ tx+sx, ty+sy, tx+s2x, ty+s2y }); + break; + case JOIN_ROUND: + float theta0 = acos((sx*s2x+sy*s2y)/(sx*sx+sy*sy)); + for(int i=0; i<JOINSTEPS; i++) { + float theta = theta0*i/(JOINSTEPS-1); + float sint = sin(theta), cost = cos(theta); + left += ({ tx+sx*cost+sy*sint, ty+sy*cost-sx*sint }); + } + break; + } + + /* Right join */ + + switch(mdiv>0.0 && join_style) { + case JOIN_MITER: + right += ({ tx-sx-sy*m, ty-sy+sx*m }); + break; + case JOIN_BEVEL: + right += ({ tx-sx, ty-sy, tx-s2x, ty-s2y }); + break; + case JOIN_ROUND: + float theta0 = -acos((sx*s2x+sy*s2y)/(sx*sx+sy*sy)); + for(int i=0; i<JOINSTEPS; i++) { + float theta = theta0*i/(JOINSTEPS-1); + float sint = sin(theta), cost = cos(theta); + right += ({ tx-sx*cost-sy*sint, ty-sy*cost+sx*sint }); + } + break; + } + } + + } else { + + /* Final cap */ + + switch(cap_style) { + case CAP_BUTT: + left += ({ ox+sx, oy+sy }); + right += ({ ox-sx, oy-sy }); + break; + case CAP_PROJECTING: + left += ({ ox+sx+sy, oy+sy-sx }); + right += ({ ox-sx+sy, oy-sy-sx }); + break; + case CAP_ROUND: + array(float) end_cap = allocate(CAPSTEPS*2); + + int j=0; + for(int i=0; i<CAPSTEPS; i++) { + end_cap[j++] = ox - sx*cap_cos_table[i] + sy*cap_sin_table[i]; + end_cap[j++] = oy - sy*cap_cos_table[i] - sx*cap_sin_table[i]; + } + right += end_cap; + break; + } + + } + + } + + if(closed) + return ({ left, right }); + else + return ({ left + xyreverse(right) }); + +} diff --git a/lib/modules/Graphics.pmod/Graph.pmod/test.pike b/lib/modules/Graphics.pmod/Graph.pmod/test.pike new file mode 100755 index 0000000000000000000000000000000000000000..f09eba3c80f30268a3479caa198bc233e66c9ee7 --- /dev/null +++ b/lib/modules/Graphics.pmod/Graph.pmod/test.pike @@ -0,0 +1,10 @@ +#!/usr/local/bin/pike-hubbe + +//This should not be a part of this module? +import "."; + +void main() +{ + Image.Image image=Diagram.bars((["data":({({1.0,2.0,1.0,2.0})})])); + Stdio.write_file("dia.gif", Image.GIF.encode(image)); +}