1 /* This notice must be untouched at all times.
\r
3 wz_jsgraphics.js v. 2.36
\r
4 The latest version is available at
\r
5 http://www.walterzorn.com
\r
6 or http://www.devira.com
\r
7 or http://www.walterzorn.de
\r
9 Copyright (c) 2002-2008 Walter Zorn. All rights reserved.
\r
10 Created 3. 11. 2002 by Walter Zorn (Web: http://www.walterzorn.com )
\r
11 Last modified: 21. 6. 2006
\r
13 Performance optimizations for Internet Explorer
\r
14 by Thomas Frank and John Holdsworth.
\r
15 fillPolygon method implemented by Matthieu Haller.
\r
17 High Performance JavaScript Graphics Library.
\r
19 - to draw lines, rectangles, ellipses, polygons
\r
20 with specifiable line thickness,
\r
21 - to fill rectangles and ellipses
\r
23 NOTE: Operations, functions and branching have rather been optimized
\r
24 to efficiency and speed than to shortness of source code.
\r
28 This library is free software; you can redistribute it and/or
\r
29 modify it under the terms of the GNU Lesser General Public
\r
30 License (LGPL) as published by the Free Software Foundation; either
\r
31 version 2.1 of the License, or (at your option) any later version.
\r
33 This library is distributed in the hope that it will be useful,
\r
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
36 Lesser General Public License for more details.
\r
38 You should have received a copy of the GNU Lesser General Public
\r
39 License along with this library; if not, write to the Free Software
\r
40 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
\r
41 or see http://www.gnu.org/copyleft/lesser.html
\r
45 var jg_ihtm, jg_ie, jg_fast, jg_dom, jg_moz,
\r
46 jg_n4 = (document.layers && typeof document.classes != "undefined");
\r
49 function chkDHTM(x, i)
\r
51 x = document.body || null;
\r
52 jg_ie = x && typeof x.insertAdjacentHTML != "undefined";
\r
53 jg_dom = (x && !jg_ie &&
\r
54 typeof x.appendChild != "undefined" &&
\r
55 typeof document.createRange != "undefined" &&
\r
56 typeof (i = document.createRange()).setStartBefore != "undefined" &&
\r
57 typeof i.createContextualFragment != "undefined");
\r
58 jg_ihtm = !jg_ie && !jg_dom && x && typeof x.innerHTML != "undefined";
\r
59 jg_fast = jg_ie && document.all && !window.opera;
\r
60 jg_moz = jg_dom && typeof x.style.MozOpacity != "undefined";
\r
66 this.wnd.document.write(jg_fast? this.htmRpc() : this.htm);
\r
71 function pntCnvDom()
\r
73 var x = this.wnd.document.createRange();
\r
74 x.setStartBefore(this.cnv);
\r
75 x = x.createContextualFragment(jg_fast? this.htmRpc() : this.htm);
\r
76 if(this.cnv) this.cnv.appendChild(x);
\r
83 if(this.cnv) this.cnv.insertAdjacentHTML("BeforeEnd", jg_fast? this.htmRpc() : this.htm);
\r
88 function pntCnvIhtm()
\r
90 if(this.cnv) this.cnv.innerHTML += this.htm;
\r
101 function mkDiv(x, y, w, h)
\r
103 this.htm += '<div style="position:absolute;'+
\r
104 'left:' + x + 'px;'+
\r
105 'top:' + y + 'px;'+
\r
106 'width:' + w + 'px;'+
\r
107 'height:' + h + 'px;'+
\r
108 'clip:rect(0,'+w+'px,'+h+'px,0);'+
\r
109 'background-color:' + this.color +
\r
110 (!jg_moz? ';overflow:hidden' : '')+
\r
115 function mkDivIe(x, y, w, h)
\r
117 this.htm += '%%'+this.color+';'+x+';'+y+';'+w+';'+h+';';
\r
121 function mkDivPrt(x, y, w, h)
\r
123 this.htm += '<div style="position:absolute;'+
\r
124 'border-left:' + w + 'px solid ' + this.color + ';'+
\r
125 'left:' + x + 'px;'+
\r
126 'top:' + y + 'px;'+
\r
128 'height:' + h + 'px;'+
\r
129 'clip:rect(0,'+w+'px,'+h+'px,0);'+
\r
130 'background-color:' + this.color +
\r
131 (!jg_moz? ';overflow:hidden' : '')+
\r
136 function mkLyr(x, y, w, h)
\r
138 this.htm += '<layer '+
\r
139 'left="' + x + '" '+
\r
140 'top="' + y + '" '+
\r
141 'width="' + w + '" '+
\r
142 'height="' + h + '" '+
\r
143 'bgcolor="' + this.color + '"><\/layer>\n';
\r
147 var regex = /%%([^;]+);([^;]+);([^;]+);([^;]+);([^;]+);/g;
\r
150 return this.htm.replace(
\r
152 '<div style="overflow:hidden;position:absolute;background-color:'+
\r
153 '$1;left:$2;top:$3;width:$4;height:$5"></div>\n');
\r
157 function htmPrtRpc()
\r
159 return this.htm.replace(
\r
161 '<div style="overflow:hidden;position:absolute;background-color:'+
\r
162 '$1;left:$2;top:$3;width:$4;height:$5;border-left:$4px solid $1"></div>\n');
\r
166 function mkLin(x1, y1, x2, y2)
\r
177 var dx = x2-x1, dy = Math.abs(y2-y1),
\r
179 yIncr = (y1 > y2)? -1 : 1;
\r
184 pru = pr - (dx<<1),
\r
192 this.mkDiv(ox, y, x-ox, 1);
\r
199 this.mkDiv(ox, y, x2-ox+1, 1);
\r
205 pru = pr - (dy<<1),
\r
214 this.mkDiv(x++, y, 1, oy-y+1);
\r
225 this.mkDiv(x2, y2, 1, oy-y2+1);
\r
234 this.mkDiv(x++, oy, 1, y-oy);
\r
240 this.mkDiv(x2, oy, 1, y2-oy+1);
\r
246 function mkLin2D(x1, y1, x2, y2)
\r
257 var dx = x2-x1, dy = Math.abs(y2-y1),
\r
259 yIncr = (y1 > y2)? -1 : 1;
\r
261 var s = this.stroke;
\r
264 if (dx > 0 && s-3 > 0)
\r
266 var _s = (s*dx*Math.sqrt(1+dy*dy/(dx*dx))-dx-(s>>1)*dy) / dx;
\r
267 _s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;
\r
270 var ad = Math.ceil(s/2);
\r
273 pru = pr - (dx<<1),
\r
281 this.mkDiv(ox, y, x-ox+ad, _s);
\r
288 this.mkDiv(ox, y, x2-ox+ad+1, _s);
\r
295 var _s = (s*dy*Math.sqrt(1+dx*dx/(dy*dy))-(s>>1)*dx-dy) / dy;
\r
296 _s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;
\r
299 var ad = Math.round(s/2);
\r
302 pru = pr - (dy<<1),
\r
312 this.mkDiv(x++, y, _s, oy-y+ad);
\r
323 this.mkDiv(x2, y2, _s, oy-y2+ad);
\r
332 this.mkDiv(x++, oy, _s, y-oy+ad);
\r
338 this.mkDiv(x2, oy, _s, y2-oy+ad+1);
\r
344 function mkLinDott(x1, y1, x2, y2)
\r
355 var dx = x2-x1, dy = Math.abs(y2-y1),
\r
357 yIncr = (y1 > y2)? -1 : 1,
\r
362 pru = pr - (dx<<1),
\r
366 if (drw) this.mkDiv(x, y, 1, 1);
\r
376 if (drw) this.mkDiv(x, y, 1, 1);
\r
382 pru = pr - (dy<<1),
\r
386 if (drw) this.mkDiv(x, y, 1, 1);
\r
396 if (drw) this.mkDiv(x, y, 1, 1);
\r
401 function mkOv(left, top, width, height)
\r
403 var a = width>>1, b = height>>1,
\r
404 wod = width&1, hod = (height&1)+1,
\r
405 cx = left+a, cy = top+b,
\r
408 aa = (a*a)<<1, bb = (b*b)<<1,
\r
409 st = (aa>>1)*(1-(b<<1)) + bb,
\r
410 tt = (bb>>1) - aa*((b<<1)-1),
\r
416 st += bb*((x<<1)+3);
\r
417 tt += (bb<<1)*(++x);
\r
421 st += bb*((x<<1)+3) - (aa<<1)*(y-1);
\r
422 tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
\r
427 this.mkOvQds(cx, cy, -x+2, ox+wod, -oy, oy-1+hod, 1, 1);
\r
428 this.mkOvQds(cx, cy, -x+1, x-1+wod, -y-1, y+hod, 1, 1);
\r
430 else this.mkOvQds(cx, cy, -x+1, ox+wod, -oy, oy-h+hod, w, h);
\r
436 tt -= aa*((y<<1)-3);
\r
437 st -= (aa<<1)*(--y);
\r
440 this.mkDiv(cx-a, cy-oy, a-ox+1, (oy<<1)+hod);
\r
441 this.mkDiv(cx+ox+wod, cy-oy, a-ox+1, (oy<<1)+hod);
\r
445 function mkOv2D(left, top, width, height)
\r
447 var s = this.stroke;
\r
450 var a = width>>1, b = height>>1,
\r
451 wod = width&1, hod = (height&1)+1,
\r
452 cx = left+a, cy = top+b,
\r
454 aa = (a*a)<<1, bb = (b*b)<<1,
\r
455 st = (aa>>1)*(1-(b<<1)) + bb,
\r
456 tt = (bb>>1) - aa*((b<<1)-1);
\r
458 if (s-4 < 0 && (!(s-2) || width-51 > 0 && height-51 > 0))
\r
460 var ox = 0, oy = b,
\r
462 pxl, pxr, pxt, pxb, pxw;
\r
467 st += bb*((x<<1)+3);
\r
468 tt += (bb<<1)*(++x);
\r
472 st += bb*((x<<1)+3) - (aa<<1)*(y-1);
\r
473 tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
\r
488 this.mkOvQds(cx, cy, -x+1, ox-pxw+w+wod, -oy, -h+oy+hod, pxw, h);
\r
494 tt -= aa*((y<<1)-3);
\r
495 st -= (aa<<1)*(--y);
\r
498 this.mkDiv(cx-a, cy-oy, s, (oy<<1)+hod);
\r
499 this.mkDiv(cx+a+wod-s+1, cy-oy, s, (oy<<1)+hod);
\r
504 var _a = (width-((s-1)<<1))>>1,
\r
505 _b = (height-((s-1)<<1))>>1,
\r
507 _aa = (_a*_a)<<1, _bb = (_b*_b)<<1,
\r
508 _st = (_aa>>1)*(1-(_b<<1)) + _bb,
\r
509 _tt = (_bb>>1) - _aa*((_b<<1)-1),
\r
513 _pxb = new Array();
\r
521 st += bb*((x<<1)+3);
\r
522 tt += (bb<<1)*(++x);
\r
523 pxl[pxl.length] = x;
\r
524 pxt[pxt.length] = y;
\r
528 st += bb*((x<<1)+3) - (aa<<1)*(y-1);
\r
529 tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
\r
530 pxl[pxl.length] = x;
\r
531 pxt[pxt.length] = y;
\r
535 tt -= aa*((y<<1)-3);
\r
536 st -= (aa<<1)*(--y);
\r
543 _st += _bb*((_x<<1)+3);
\r
544 _tt += (_bb<<1)*(++_x);
\r
545 _pxb[_pxb.length] = _y-1;
\r
549 _st += _bb*((_x<<1)+3) - (_aa<<1)*(_y-1);
\r
550 _tt += (_bb<<1)*(++_x) - _aa*(((_y--)<<1)-3);
\r
551 _pxb[_pxb.length] = _y-1;
\r
555 _tt -= _aa*((_y<<1)-3);
\r
556 _st -= (_aa<<1)*(--_y);
\r
557 _pxb[_pxb.length-1]--;
\r
562 var ox = 0, oy = b,
\r
566 for (var i = 0; i < l; i++)
\r
568 if (typeof _pxb[i] != "undefined")
\r
570 if (_pxb[i] < _oy || pxt[i] < oy)
\r
573 this.mkOvQds(cx, cy, -x+1, ox+wod, -oy, _oy+hod, x-ox, oy-_oy);
\r
582 this.mkDiv(cx-x+1, cy-oy, 1, (oy<<1)+hod);
\r
583 this.mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);
\r
588 this.mkDiv(cx-a, cy-oy, 1, (oy<<1)+hod);
\r
589 this.mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);
\r
594 function mkOvDott(left, top, width, height)
\r
596 var a = width>>1, b = height>>1,
\r
597 wod = width&1, hod = height&1,
\r
598 cx = left+a, cy = top+b,
\r
600 aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1,
\r
601 st = (aa2>>1)*(1-(b<<1)) + bb,
\r
602 tt = (bb>>1) - aa2*((b<<1)-1),
\r
608 st += bb*((x<<1)+3);
\r
609 tt += (bb<<1)*(++x);
\r
613 st += bb*((x<<1)+3) - aa4*(y-1);
\r
614 tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3);
\r
618 tt -= aa2*((y<<1)-3);
\r
621 if (drw) this.mkOvQds(cx, cy, -x, x+wod, -y, y+hod, 1, 1);
\r
627 function mkRect(x, y, w, h)
\r
629 var s = this.stroke;
\r
630 this.mkDiv(x, y, w, s);
\r
631 this.mkDiv(x+w, y, s, h);
\r
632 this.mkDiv(x, y+h, w+s, s);
\r
633 this.mkDiv(x, y+s, s, h-s);
\r
637 function mkRectDott(x, y, w, h)
\r
639 this.drawLine(x, y, x+w, y);
\r
640 this.drawLine(x+w, y, x+w, y+h);
\r
641 this.drawLine(x, y+h, x+w, y+h);
\r
642 this.drawLine(x, y, x, y+h);
\r
648 this.PLAIN = 'font-weight:normal;';
\r
649 this.BOLD = 'font-weight:bold;';
\r
650 this.ITALIC = 'font-style:italic;';
\r
651 this.ITALIC_BOLD = this.ITALIC + this.BOLD;
\r
652 this.BOLD_ITALIC = this.ITALIC_BOLD;
\r
654 var Font = new jsgFont();
\r
657 function jsgStroke()
\r
661 var Stroke = new jsgStroke();
\r
664 function jsGraphics(id, wnd)
\r
666 this.setColor = new Function('arg', 'this.color = arg.toLowerCase();');
\r
668 this.setStroke = function(x)
\r
673 this.drawLine = mkLinDott;
\r
674 this.mkOv = mkOvDott;
\r
675 this.drawRect = mkRectDott;
\r
679 this.drawLine = mkLin2D;
\r
680 this.mkOv = mkOv2D;
\r
681 this.drawRect = mkRect;
\r
685 this.drawLine = mkLin;
\r
687 this.drawRect = mkRect;
\r
692 this.setPrintable = function(arg)
\r
694 this.printable = arg;
\r
697 this.mkDiv = mkDivIe;
\r
698 this.htmRpc = arg? htmPrtRpc : htmRpc;
\r
700 else this.mkDiv = jg_n4? mkLyr : arg? mkDivPrt : mkDiv;
\r
704 this.setFont = function(fam, sz, sty)
\r
708 this.ftSty = sty || Font.PLAIN;
\r
712 this.drawPolyline = this.drawPolyLine = function(x, y, s)
\r
714 for (var i=0 ; i<x.length-1 ; i++ )
\r
715 this.drawLine(x[i], y[i], x[i+1], y[i+1]);
\r
719 this.fillRect = function(x, y, w, h)
\r
721 this.mkDiv(x, y, w, h);
\r
725 this.drawPolygon = function(x, y)
\r
727 this.drawPolyline(x, y);
\r
728 this.drawLine(x[x.length-1], y[x.length-1], x[0], y[0]);
\r
732 this.drawEllipse = this.drawOval = function(x, y, w, h)
\r
734 this.mkOv(x, y, w, h);
\r
738 this.fillEllipse = this.fillOval = function(left, top, w, h)
\r
740 var a = (w -= 1)>>1, b = (h -= 1)>>1,
\r
741 wod = (w&1)+1, hod = (h&1)+1,
\r
742 cx = left+a, cy = top+b,
\r
745 aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1,
\r
746 st = (aa2>>1)*(1-(b<<1)) + bb,
\r
747 tt = (bb>>1) - aa2*((b<<1)-1),
\r
749 if (w+1) while (y > 0)
\r
753 st += bb*((x<<1)+3);
\r
754 tt += (bb<<1)*(++x);
\r
758 st += bb*((x<<1)+3) - aa4*(y-1);
\r
761 tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3);
\r
763 this.mkDiv(pxl, cy-oy, dw, dh);
\r
764 this.mkDiv(pxl, cy+y+hod, dw, dh);
\r
770 tt -= aa2*((y<<1)-3);
\r
774 this.mkDiv(cx-a, cy-oy, w+1, (oy<<1)+hod);
\r
778 /* fillPolygon method, implemented by Matthieu Haller.
\r
779 This javascript function is an adaptation of the gdImageFilledPolygon for Walter Zorn lib.
\r
780 C source of GD 1.8.4 found at http://www.boutell.com/gd/
\r
782 THANKS to Kirsten Schulz for the polygon fixes!
\r
784 The intersection finding technique of this code could be improved
\r
785 by remembering the previous intertersection, and by using the slope.
\r
786 That could help to adjust intersections to produce a nice
\r
787 interior_extrema. */
\r
788 this.fillPolygon = function(array_x, array_y)
\r
798 var n = array_x.length;
\r
805 for (i = 1; i < n; i++)
\r
807 if (array_y[i] < miny)
\r
810 if (array_y[i] > maxy)
\r
813 for (y = miny; y <= maxy; y++)
\r
815 var polyInts = new Array();
\r
817 for (i = 0; i < n; i++)
\r
829 y1 = array_y[ind1];
\r
830 y2 = array_y[ind2];
\r
833 x1 = array_x[ind1];
\r
834 x2 = array_x[ind2];
\r
838 y2 = array_y[ind1];
\r
839 y1 = array_y[ind2];
\r
840 x2 = array_x[ind1];
\r
841 x1 = array_x[ind2];
\r
845 // modified 11. 2. 2004 Walter Zorn
\r
846 if ((y >= y1) && (y < y2))
\r
847 polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);
\r
849 else if ((y == maxy) && (y > y1) && (y <= y2))
\r
850 polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);
\r
852 polyInts.sort(integer_compare);
\r
853 for (i = 0; i < ints; i+=2)
\r
854 this.mkDiv(polyInts[i], y, polyInts[i+1]-polyInts[i]+1, 1);
\r
859 this.drawString = function(txt, x, y)
\r
861 this.htm += '<div style="position:absolute;white-space:nowrap;'+
\r
862 'left:' + x + 'px;'+
\r
863 'top:' + y + 'px;'+
\r
864 'font-family:' + this.ftFam + ';'+
\r
865 'font-size:' + this.ftSz + ';'+
\r
866 'color:' + this.color + ';' + this.ftSty + '">'+
\r
872 /* drawStringRect() added by Rick Blommers.
\r
873 Allows to specify the size of the text rectangle and to align the
\r
874 text both horizontally (e.g. right) and vertically within that rectangle */
\r
875 this.drawStringRect = function(txt, x, y, width, halign)
\r
877 this.htm += '<div style="position:absolute;overflow:hidden;'+
\r
878 'left:' + x + 'px;'+
\r
879 'top:' + y + 'px;'+
\r
880 'width:'+width +'px;'+
\r
881 'text-align:'+halign+';'+
\r
882 'font-family:' + this.ftFam + ';'+
\r
883 'font-size:' + this.ftSz + ';'+
\r
884 'color:' + this.color + ';' + this.ftSty + '">'+
\r
890 this.drawImage = function(imgSrc, x, y, w, h, a)
\r
892 this.htm += '<div style="position:absolute;'+
\r
893 'left:' + x + 'px;'+
\r
894 'top:' + y + 'px;'+
\r
895 'width:' + w + 'px;'+
\r
896 'height:' + h + 'px;">'+
\r
897 '<img src="' + imgSrc + '" width="' + w + '" height="' + h + '"' + (a? (' '+a) : '') + '>'+
\r
902 this.clear = function()
\r
905 if (this.cnv) this.cnv.innerHTML = this.defhtm;
\r
909 this.mkOvQds = function(cx, cy, xl, xr, yt, yb, w, h)
\r
911 this.mkDiv(xr+cx, yt+cy, w, h);
\r
912 this.mkDiv(xr+cx, yb+cy, w, h);
\r
913 this.mkDiv(xl+cx, yb+cy, w, h);
\r
914 this.mkDiv(xl+cx, yt+cy, w, h);
\r
918 this.setFont('verdana,geneva,helvetica,sans-serif', String.fromCharCode(0x31, 0x32, 0x70, 0x78), Font.PLAIN);
\r
919 this.color = '#000000';
\r
921 this.wnd = wnd || window;
\r
923 if (!(jg_ie || jg_dom || jg_ihtm)) chkDHTM();
\r
924 if (typeof id != 'string' || !id) this.paint = pntDoc;
\r
927 this.cnv = document.all? (this.wnd.document.all[id] || null)
\r
928 : document.getElementById? (this.wnd.document.getElementById(id) || null)
\r
930 this.defhtm = (this.cnv && this.cnv.innerHTML)? this.cnv.innerHTML : '';
\r
931 this.paint = jg_dom? pntCnvDom : jg_ie? pntCnvIe : jg_ihtm? pntCnvIhtm : pntCnv;
\r
934 this.setPrintable(false);
\r
939 function integer_compare(x,y)
\r
941 return (x < y) ? -1 : ((x > y)*1);
\r