]> git.lyx.org Git - features.git/blob - src/mathed/support.C
iterators for MathArray; cosmetics;
[features.git] / src / mathed / support.C
1 #include <config.h>
2
3 #include <algorithm>
4
5 #include "mathed/support.h"
6 #include "lyxfont.h"
7 #include "font.h"
8 #include "math_defs.h"
9 #include "math_parser.h"
10 #include "Painter.h"
11 #include "debug.h"
12
13 using std::sort;
14 using std::lower_bound;
15 using std::endl;
16 using std::max;
17
18
19 bool MathIsAlphaFont(MathTextCodes x)
20 {
21         return LM_TC_VAR <= x && x <= LM_TC_TEXTRM;
22 }
23
24
25 bool MathIsBinary(MathTextCodes x)
26 {
27         return x == LM_TC_BOP;
28 }
29
30
31 bool MathIsSymbol(MathTextCodes x)
32 {
33         return x == LM_TC_SYMB || x == LM_TC_BSYM;
34 }
35      
36
37
38 ///
39 class Matrix {
40 public:
41         ///
42         typedef float matriz_data[2][2];
43         ///
44         Matrix();
45         ///
46         void rotate(int);
47         ///
48         void escalate(float, float);
49         ///
50         void transform(float, float, float &, float &);
51 private:
52         ///
53         matriz_data m_;
54         ///
55         void multiply(matriz_data & a);
56 };
57
58 Matrix::Matrix()
59 {
60         m_[0][0] = 1;
61         m_[0][1] = 0;
62         m_[1][0] = 0;
63         m_[1][1] = 1;
64 }
65
66 void Matrix::rotate(int code)
67 {
68         matriz_data r;
69         r[0][0] = 1;
70         r[0][1] = 0;
71         r[1][0] = 0;
72         r[1][1] = 1;
73         float const cs = (code & 1) ? 0 : (1 - code);
74         float const sn = (code & 1) ? (2 - code) : 0;
75         r[0][0] = cs;
76         r[0][1] = sn;
77         r[1][0] = -r[0][1];
78         r[1][1] = r[0][0];
79         multiply(r);
80 }
81
82 void Matrix::escalate(float x, float y)
83 {
84         matriz_data s;
85         s[0][0] = x;
86         s[0][1] = 0;
87         s[1][0] = 0;
88         s[1][1] = y;
89         multiply(s);
90 }
91
92 void Matrix::multiply(matriz_data & a)
93 {
94         matriz_data c;
95         c[0][0] = a[0][0] * m_[0][0] + a[0][1] * m_[1][0];
96         c[1][0] = a[1][0] * m_[0][0] + a[1][1] * m_[1][0];
97         c[0][1] = a[0][0] * m_[0][1] + a[0][1] * m_[1][1];
98         c[1][1] = a[1][0] * m_[0][1] + a[1][1] * m_[1][1];
99         m_[0][0] = c[0][0];     
100         m_[0][1] = c[0][1];     
101         m_[1][0] = c[1][0];     
102         m_[1][1] = c[1][1];     
103 }
104
105 void Matrix::transform(float xp, float yp, float & x, float & y)
106 {
107         x = m_[0][0] * xp + m_[0][1] * yp;
108         y = m_[1][0] * xp + m_[1][1] * yp;
109 }
110
111
112 namespace {
113
114 LyXFont           * Math_Fonts = 0;
115
116 void mathed_init_fonts()
117 {
118         Math_Fonts = new LyXFont[8]; //DEC cxx cannot initialize all fonts
119         //at once (JMarc) rc
120
121         for (int i = 0 ; i < 8 ; ++i) {
122                 Math_Fonts[i] = LyXFont(LyXFont::ALL_SANE);
123         }
124
125         Math_Fonts[0].setShape(LyXFont::ITALIC_SHAPE);
126
127         Math_Fonts[1].setFamily(LyXFont::SYMBOL_FAMILY);
128
129         Math_Fonts[2].setFamily(LyXFont::SYMBOL_FAMILY);
130         Math_Fonts[2].setShape(LyXFont::ITALIC_SHAPE);
131
132         Math_Fonts[3].setSeries(LyXFont::BOLD_SERIES);
133
134         Math_Fonts[4].setFamily(LyXFont::SANS_FAMILY);
135         Math_Fonts[4].setShape(LyXFont::ITALIC_SHAPE);
136
137         Math_Fonts[5].setFamily(LyXFont::TYPEWRITER_FAMILY);
138
139         Math_Fonts[6].setFamily(LyXFont::ROMAN_FAMILY);
140
141         Math_Fonts[7].setFamily(LyXFont::SANS_FAMILY);
142 }
143
144 } // namespace
145
146
147 LyXFont WhichFont(MathTextCodes type, MathStyles size)
148 {
149         LyXFont f;
150         
151         if (!Math_Fonts)
152                 mathed_init_fonts();
153
154         switch (type) {
155         case LM_TC_SYMB:        
156         case LM_TC_BSYM:        
157                 f = Math_Fonts[2];
158                 break;
159
160         case LM_TC_VAR:
161         case LM_TC_IT:
162                 f = Math_Fonts[0];
163                 break;
164
165         case LM_TC_BF:
166                 f = Math_Fonts[3];
167                 break;
168
169         case LM_TC_SF:
170                 f = Math_Fonts[7];
171                 break;
172
173         case LM_TC_CAL:
174                 f = Math_Fonts[4];
175                 break;
176
177         case LM_TC_TT:
178                 f = Math_Fonts[5];
179                 break;
180
181         case LM_TC_SPECIAL: //f = Math_Fonts[0]; break;
182         case LM_TC_TEXTRM:
183         case LM_TC_TEX:
184         case LM_TC_RM:
185                 f = Math_Fonts[6];
186                 break;
187
188         default:
189                 f = Math_Fonts[1];
190                 break;
191         }
192
193         switch (size) {
194         case LM_ST_DISPLAY:
195                 if (type == LM_TC_BSYM) {
196                         f.incSize();
197                         f.incSize();
198                 }
199                 break;
200
201         case LM_ST_TEXT:
202                 break;
203
204         case LM_ST_SCRIPT:
205                 f.decSize();
206                 f.decSize();
207                 break;
208
209         case LM_ST_SCRIPTSCRIPT:
210                 f.decSize();
211                 f.decSize();
212                 f.decSize();
213                 break;
214
215         default:
216                 lyxerr << "Math Error: wrong font size: " << size << endl;
217                 break;
218         }
219
220         if (type != LM_TC_TEXTRM)
221                 f.setColor(LColor::math);
222
223         if (type == LM_TC_TEX)
224                 f.setColor(LColor::latex);
225
226         return f;
227 }
228
229 char const * math_font_name[] = {
230         "mathrm",
231         "mathcal",
232         "mathbf",
233         "mathsf",
234         "mathtt",
235         "mathit",
236         "textrm"
237 };
238
239
240 char const * latex_mathspace[] = {
241         "!", ",", ":", ";", "quad", "qquad"
242 };
243
244
245 namespace {
246
247 /* 
248  * Internal struct of a drawing: code n x1 y1 ... xn yn, where code is:
249  * 0 = end, 1 = line, 2 = polyline, 3 = square line, 4= square polyline
250  */
251
252
253 float const parenthHigh[] = {
254         2.0, 13.0,
255         0.9840, 0.0014, 0.7143, 0.0323, 0.4603, 0.0772,
256         0.2540, 0.1278, 0.1746, 0.1966, 0.0952, 0.3300,
257         0.0950, 0.5000, 0.0952, 0.6700, 0.1746, 0.8034,
258         0.2540, 0.8722, 0.4603, 0.9228, 0.7143, 0.9677,
259         0.9840, 0.9986,
260         0.0 
261 };
262
263
264 float const parenth[] = {
265         2.0, 13.0,
266         0.9930, 0.0071, 0.7324, 0.0578, 0.5141, 0.1126,
267         0.3380, 0.1714, 0.2183, 0.2333, 0.0634, 0.3621,
268         0.0141, 0.5000, 0.0563, 0.6369, 0.2113, 0.7647,
269         0.3310, 0.8276, 0.5070, 0.8864, 0.7254, 0.9412,
270         0.9930, 0.9919,
271         0.0   
272 };
273
274
275 float const brace[] = {
276         2.0, 21.0,
277         0.9492, 0.0020, 0.9379, 0.0020, 0.7458, 0.0243,
278         0.5819, 0.0527, 0.4859, 0.0892, 0.4463, 0.1278,
279         0.4463, 0.3732, 0.4011, 0.4199, 0.2712, 0.4615,
280         0.0734, 0.4919, 0.0113, 0.5000, 0.0734, 0.5081,
281         0.2712, 0.5385, 0.4011, 0.5801, 0.4463, 0.6268,
282         0.4463, 0.8722, 0.4859, 0.9108, 0.5819, 0.9473,
283         0.7458, 0.9757, 0.9379, 0.9980, 0.9492, 0.9980,
284         0.0
285 };
286
287
288 // Is this correct? (Lgb)
289 float const arrow[] = {
290         4, 7,
291         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
292         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
293         0.9500, 0.7500,
294         3, 0.5000, 0.1500, 0.5000, 0.9500,
295         0.0 
296 };
297
298
299 // Is this correct? (Lgb)
300 float const Arrow[] = {
301         4, 7,
302         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
303         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
304         0.9500, 0.7500,
305         3, 0.3500, 0.5000, 0.3500, 0.9500,
306         3, 0.6500, 0.5000, 0.6500, 0.9500,
307         0.0
308 };
309
310
311 float const udarrow[] = {
312         2, 3,
313         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
314         2, 3,
315         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,  
316         1, 0.5, 0.2,  0.5, 0.8,
317         0.0 
318 };
319
320
321 float const Udarrow[] = {
322         2, 3,
323         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
324         2, 3,
325         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,  
326         1, 0.35, 0.2, 0.35, 0.8,
327         1, 0.65, 0.2, 0.65, 0.8,
328         0.0 
329 };
330
331
332 float const brack[] = {
333         2.0, 4,
334         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,  0.95, 0.95,
335         0.0
336 };
337
338
339 float const corner[] = {
340         2.0, 3,
341         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,
342         0.0
343 };
344
345
346 float const angle[] = {
347         2.0, 3,
348         1, 0,  0.05, 0.5,  1, 1,
349         0.0
350 };
351
352
353 float const slash[] = {
354         1, 0.95, 0.05,  0.05, 0.95, 
355         0.0
356 };
357
358
359 float const hline[] = {
360         1, 0.05, 0.5,  0.95, 0.5, 
361         0.0
362 };
363
364
365 float const hline2[] = {
366    1, 0.1, 0.5,  0.3, 0.5,
367    1, 0.7, 0.5,  0.9, 0.5,
368    0.0
369 }; 
370
371
372 float const hline3[] = {
373         1, 0.1, 0,  0.15, 0,
374         1, 0.475, 0,  0.525, 0,
375         1, 0.85, 0,  0.9, 0,  
376         0.0
377 };
378
379
380 float const dline3[] = {
381         1, 0.1, 0.1,  0.15, 0.15,
382         1, 0.475, 0.475,  0.525, 0.525,
383         1, 0.85, 0.85,  0.9, 0.9,
384         0.0
385 };     
386
387
388 float const hlinesmall[] = {
389         1, 0.4, 0.5,  0.6, 0.5, 
390         0.0
391 };
392
393
394 float const vert[] = {
395         1, 0.5, 0.05,  0.5, 0.95, 
396         0.0
397 };
398
399
400 float const  Vert[] = {
401         1, 0.3, 0.05,  0.3, 0.95, 
402         1, 0.7, 0.05,  0.7, 0.95,
403         0.0
404 };
405
406
407 float const tilde[] = {
408         2.0, 4,
409         0.05, 0.8,  0.25, 0.2,  0.75, 0.8,  0.95, 0.2,
410         0.0
411 };
412
413
414 struct math_deco_struct {
415         int code;
416         float const * data;
417         int angle;
418 };
419
420 math_deco_struct math_deco_table[] = {   
421         // Decorations
422         { LM_widehat,       &angle[0],      3 },
423         { LM_widetilde,     &tilde[0],      0 },
424         { LM_underline,     &hline[0],      0 },
425         { LM_overline,      &hline[0],      0 },
426         { LM_underbrace,    &brace[0],      1 },
427         { LM_overbrace,     &brace[0],      3 },
428         { LM_overleftarrow, &arrow[0],      1 },
429         { LM_overightarrow, &arrow[0],      3 },
430                                             
431         // Delimiters                       
432         { '(',              &parenth[0],    0 },
433         { ')',              &parenth[0],    2 },
434         { '{',              &brace[0],      0 },
435         { '}',              &brace[0],      2 },
436         { '[',              &brack[0],      0 },
437         { ']',              &brack[0],      2 },
438         { '|',              &vert[0],       0 },
439         { '/',              &slash[0],      0 },
440         { LM_Vert,          &Vert[0],       0 },
441         { LM_backslash,     &slash[0],      1 },
442         { LM_langle,        &angle[0],      0 },
443         { LM_lceil,         &corner[0],     0 }, 
444         { LM_lfloor,        &corner[0],     1 },  
445         { LM_rangle,        &angle[0],      2 }, 
446         { LM_rceil,         &corner[0],     3 }, 
447         { LM_rfloor,        &corner[0],     2 },
448         { LM_downarrow,     &arrow[0],      2 },
449         { LM_Downarrow,     &Arrow[0],      2 }, 
450         { LM_uparrow,       &arrow[0],      0 },
451         { LM_Uparrow,       &Arrow[0],      0 },
452         { LM_updownarrow,   &udarrow[0],    0 },
453         { LM_Updownarrow,   &Udarrow[0],    0 },         
454                                             
455         // Accents                          
456         { LM_ddot,          &hline2[0],     0 },
457         { LM_hat,           &angle[0],      3 },
458         { LM_grave,         &slash[0],      1 },
459         { LM_acute,         &slash[0],      0 },
460         { LM_tilde,         &tilde[0],      0 },
461         { LM_bar,           &hline[0],      0 },
462         { LM_dot,           &hlinesmall[0], 0 },
463         { LM_check,         &angle[0],      1 },
464         { LM_breve,         &parenth[0],    1 },
465         { LM_vec,           &arrow[0],      3 },
466         { LM_not,           &slash[0],      0 },  
467                                             
468         // Dots                             
469         { LM_ldots,         &hline3[0],     0 }, 
470         { LM_cdots,         &hline3[0],     0 },
471         { LM_vdots,         &hline3[0],     1 },
472         { LM_ddots,         &dline3[0],     0 }
473 };
474
475
476 struct math_deco_compare {
477         /// for use by sort and lower_bound
478         inline
479         int operator()(math_deco_struct const & a,
480                        math_deco_struct const & b) const {
481                 return a.code < b.code;
482         }
483 };
484
485
486 int const math_deco_table_size =
487 sizeof(math_deco_table) /sizeof(math_deco_struct);
488
489
490 class init_deco_table {
491 public:
492         init_deco_table() {
493                 if (!init) {
494                         sort(math_deco_table,
495                              math_deco_table + math_deco_table_size,
496                              math_deco_compare());
497                         init_deco_table::init = true;
498                 }
499         }
500 private:
501         static bool init;
502 };
503
504
505 bool init_deco_table::init = false;
506 static init_deco_table idt;
507
508 } // namespace anon
509
510 void mathed_char_dim(MathTextCodes type, MathStyles size, unsigned char c,
511         int & asc, int & des, int & wid)
512 {
513         LyXFont const font = WhichFont(type, size);
514         des = lyxfont::descent(c, font);
515         asc = lyxfont::ascent(c, font);
516         wid = mathed_char_width(type, size, c);
517 }
518
519 int mathed_char_height(MathTextCodes type, MathStyles size, unsigned char c,
520         int & asc, int & des)
521 {
522         LyXFont const font = WhichFont(type, size);
523         des = lyxfont::descent(c, font);
524         asc = lyxfont::ascent(c, font);
525         return asc + des;
526 }
527
528
529 int mathed_char_height(MathTextCodes type, MathStyles size, unsigned char c)
530 {
531         int asc;
532         int des;
533         return mathed_char_height(type, size, c, asc, des);
534 }
535
536 int mathed_char_ascent(MathTextCodes type, MathStyles size, unsigned char c)
537 {
538         LyXFont const font = WhichFont(type, size);
539         return lyxfont::ascent(c, font);
540 }
541
542 int mathed_char_descent(MathTextCodes type, MathStyles size, unsigned char c)
543 {
544         LyXFont const font = WhichFont(type, size);
545         return lyxfont::descent(c, font);
546 }
547
548
549
550 int mathed_char_width(MathTextCodes type, MathStyles size, unsigned char c)
551 {
552         if (MathIsBinary(type)) {
553                 string s;
554                 s += c;
555                 return mathed_string_width(type, size, s);
556         } else
557                 return lyxfont::width(c, WhichFont(type, size));
558 }
559
560
561 void mathed_string_dim(MathTextCodes type, MathStyles size, string const & s,
562                          int & asc, int & des, int & wid)
563 {
564         mathed_string_height(type, size, s, asc, des);
565         wid = mathed_string_width(type, size, s);
566 }
567
568 int mathed_string_height(MathTextCodes type, MathStyles size, string const & s,
569                          int & asc, int & des)
570 {
571         LyXFont const font = WhichFont(type, size);
572         asc = des = 0;
573         for (string::const_iterator it = s.begin(); it != s.end(); ++it) {
574                 des = max(des, lyxfont::descent(*it, font));
575                 asc = max(asc, lyxfont::ascent(*it, font));
576         }
577         return asc + des;
578 }
579
580
581 int mathed_string_width(MathTextCodes type, MathStyles size, string const & s)
582 {
583         string st;
584         if (MathIsBinary(type))
585                 for (string::const_iterator it = s.begin();
586                      it != s.end(); ++it) {
587                         st += ' ';
588                         st += *it;
589                         st += ' ';
590                 }
591         else
592                 st = s;
593         
594         return lyxfont::width(st, WhichFont(type, size));
595 }
596
597
598 namespace {
599
600 math_deco_struct const * search_deco(int code)
601 {
602         math_deco_struct search_elem = { code, 0, 0 };
603         
604         math_deco_struct const * res =
605                 lower_bound(math_deco_table,
606                             math_deco_table + math_deco_table_size,
607                             search_elem, math_deco_compare());
608         if (res != math_deco_table + math_deco_table_size &&
609             res->code == code)
610                 return res;
611         return 0;
612 }
613
614 }
615
616 void mathed_draw_deco(Painter & pain, int x, int y, int w, int h, int code)
617 {
618         Matrix mt;
619         Matrix sqmt;
620         float xx;
621         float yy;
622         float x2;
623         float y2;
624         int i = 0;
625         
626         math_deco_struct const * mds = search_deco(code);
627         if (!mds) {
628                 // Should this ever happen?
629                 lyxerr << "Deco was not found. Programming error?" << endl;
630                 return;
631         }
632         
633         int const r = mds->angle;
634         float const * d = mds->data;
635         
636         if (h > 70 && (mds->code == int('(') || mds->code == int(')')))
637                 d = parenthHigh;
638         
639         mt.rotate(r);
640         mt.escalate(w, h);
641         
642         int const n = (w < h) ? w : h;
643         sqmt.rotate(r);
644         sqmt.escalate(n, n);
645         if (r > 0 && r < 3) y += h;   
646         if (r >= 2) x += w;   
647         do {
648                 code = int(d[i++]);
649                 switch (code) {
650                 case 0: break;
651                 case 1: 
652                 case 3:
653                 {
654                         xx = d[i++]; yy = d[i++];
655                         x2 = d[i++]; y2 = d[i++];
656                         if (code == 3) 
657                                 sqmt.transform(xx, yy, xx, yy);
658                         else
659                                 mt.transform(xx, yy, xx, yy);
660                         mt.transform(x2, y2, x2, y2);
661                         pain.line(x + int(xx), y + int(yy),
662                                   x + int(x2), y + int(y2),
663                                   LColor::mathline);
664                         break;
665                 }        
666                 case 2: 
667                 case 4:
668                 {
669                         int xp[32];
670                         int yp[32];
671                         int const n = int(d[i++]);
672                         for (int j = 0; j < n; ++j) {
673                                 xx = d[i++]; yy = d[i++];
674 //           lyxerr << " " << xx << " " << yy << " ";
675                                 if (code == 4) 
676                                         sqmt.transform(xx, yy, xx, yy);
677                                 else
678                                         mt.transform(xx, yy, xx, yy);
679                                 xp[j] = x + int(xx);
680                                 yp[j] = y + int(yy);
681                                 //  lyxerr << "P[" << j " " << xx << " " << yy << " " << x << " " << y << "]";
682                         }
683                         pain.lines(xp, yp, n, LColor::mathline);
684                 }
685                 }
686         } while (code);
687 }
688
689
690
691 // In a near future maybe we use a better fonts renderer
692 void drawStr(Painter & pain, MathTextCodes type, MathStyles siz,
693         int x, int y, string const & s)
694 {
695         string st;
696         if (MathIsBinary(type))
697                 for (string::const_iterator it = s.begin();
698                      it != s.end(); ++it) {
699                         st += ' ';
700                         st += *it;
701                         st += ' ';
702                 }
703         else
704                 st = s;
705         
706         pain.text(x, y, st, WhichFont(type, siz));
707 }
708
709 void drawChar(Painter & pain, MathTextCodes type, MathStyles siz, int x, int y, char c)
710 {
711         string s;
712         s += c;
713         drawStr(pain, type, siz, x, y, s);
714 }
715
716 // decrease math size for super- and subscripts
717 MathStyles smallerStyleScript(MathStyles st)
718 {
719         switch (st) {
720                 case LM_ST_DISPLAY:
721                 case LM_ST_TEXT:    st = LM_ST_SCRIPT; break;
722                 default:            st = LM_ST_SCRIPTSCRIPT;
723         }
724         return st;
725 }
726
727 // decrease math size for fractions
728 MathStyles smallerStyleFrac(MathStyles st)
729 {
730         switch (st) {
731                 case LM_ST_DISPLAY: st = LM_ST_TEXT; break;
732                 case LM_ST_TEXT:    st = LM_ST_SCRIPT; break;
733                 default:            st = LM_ST_SCRIPTSCRIPT;
734         }
735         return st;
736 }
737
738
739 void math_font_max_dim(MathTextCodes code, MathStyles siz, int & asc, int & des)
740 {
741         LyXFont font = WhichFont(code, siz);
742         asc = lyxfont::maxAscent(font);
743         des = lyxfont::maxDescent(font);
744 }
745