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