]> git.donarmstrong.com Git - lilypond.git/blob - lily/lookup.cc
f074bab26a7c55b2b858ea5079339f83016898f9
[lilypond.git] / lily / lookup.cc
1 /*
2   lookup.cc -- implement simple Lookup methods.
3
4   source file of the GNU LilyPond music typesetter
5
6   (c)  1997--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7
8   Jan Nieuwenhuizen <janneke@gnu.org>
9
10   TODO
11       Glissando
12 */
13 #include <math.h>
14 #include <ctype.h>
15 #include "lookup.hh"
16 #include "debug.hh"
17 #include "dimensions.hh"
18
19 #include "bezier.hh"
20 #include "paper-def.hh"
21 #include "string-convert.hh"
22 #include "file-path.hh"
23 #include "main.hh"
24 #include "lily-guile.hh"
25 #include "all-font-metrics.hh"
26 #include "afm.hh"
27 #include "scope.hh"
28 #include "molecule.hh"
29 #include "atom.hh"
30 #include "lily-guile.hh"
31
32
33 Lookup::Lookup ()
34 {
35   afm_l_ = 0;  
36 }
37
38 Lookup::Lookup (Lookup const& s)
39 {
40   font_name_ = s.font_name_;
41   afm_l_ = 0;  
42 }
43
44
45
46
47 Molecule
48 Lookup::afm_find (String s, bool warn) const
49 {
50   if (!afm_l_)      
51     {
52       Lookup * me = (Lookup*)(this);
53       me->afm_l_ = all_fonts_global_p->find_afm (font_name_);
54       if (!me->afm_l_)
55         {
56           warning (_f ("Can't find font: `%s'", font_name_));
57           warning (_f ("(search path `%s')", global_path.str ().ch_C()));
58           error (_ ("Aborting"));
59         }
60     }
61   AFM_CharMetricInfo const *cm = afm_l_->find_char_metric (s, warn);
62   Molecule m;
63   if (!cm)
64     {
65       m.set_empty (false);
66       return m;
67     }
68   
69   Atom* at = new Atom (gh_list (ly_symbol2scm ("char"),
70                     gh_int2scm (cm->code),
71                     SCM_UNDEFINED));
72
73   at->fontify (afm_l_);
74   m.dim_ = afm_bbox_to_box (cm->charBBox);
75   m.add_atom (at->self_scm_);
76   return m;
77 }
78
79 Molecule
80 Lookup::simple_bar (String type, Real h, Paper_def* paper_l) const
81 {
82   SCM thick = ly_symbol2scm (("barthick_" + type).ch_C());
83   Real w = 0.0;
84   
85   if (paper_l->scope_p_->elem_b (thick))
86     {
87       w = paper_l->get_realvar (thick);
88     }
89   else
90     {
91       programming_error ("No bar thickness set ! ");
92       w = 1 PT;
93     }
94   return filledbox (Box (Interval(0,w), Interval(-h/2, h/2)));
95 }
96
97   
98 Molecule
99 Lookup::bar (String str, Real h, Paper_def *paper_l) const
100 {
101   if (str == "bracket")
102     return staff_bracket (h, paper_l);
103   else if (str == "brace")
104     {
105       Real staffht  = paper_l->get_var ("staffheight");
106       return staff_brace (h,staffht);
107     }
108   Real kern = paper_l->get_var ("bar_kern");
109   Real thinkern = paper_l->get_var ("bar_thinkern");
110
111   Molecule thin = simple_bar ("thin", h, paper_l);
112   Molecule thick = simple_bar ("thick", h, paper_l);
113   Molecule colon = afm_find ("dots-repeatcolon", paper_l);  
114
115   Molecule m;
116   
117   if (str == "")
118     {
119       return fill (Box (Interval(0, 0), Interval (-h/2, h/2)));
120     }
121   if (str == "scorepostbreak")
122     {
123       return simple_bar ("score", h, paper_l);
124     }
125   else if (str == "|")
126     {
127       return thin;
128     }
129   else if (str == "|.")
130     {
131       m.add_at_edge (X_AXIS, LEFT, thick, 0);      
132       m.add_at_edge (X_AXIS, LEFT, thin, kern);
133     }
134   else if (str == ".|")
135     {
136       m.add_at_edge (X_AXIS, RIGHT, thick, 0);
137       m.add_at_edge (X_AXIS, RIGHT, thin, kern);
138     }
139   else if (str == ":|")
140     {
141       m.add_at_edge (X_AXIS, LEFT, thick, 0);
142       m.add_at_edge (X_AXIS, LEFT, thin, kern);
143       m.add_at_edge (X_AXIS, LEFT, colon, kern);      
144     }
145   else if (str == "|:")
146     {
147       m.add_at_edge (X_AXIS, RIGHT, thick, 0);
148       m.add_at_edge (X_AXIS, RIGHT, thin, kern);
149       m.add_at_edge (X_AXIS, RIGHT, colon, kern);      
150     }
151   else if (str == ":|:")
152     {
153       m.add_at_edge (X_AXIS, LEFT, thick, thinkern);
154       m.add_at_edge (X_AXIS, LEFT, colon, kern);      
155       m.add_at_edge (X_AXIS, RIGHT, thick, kern);
156       m.add_at_edge (X_AXIS, RIGHT, colon, kern);      
157     }
158   else if (str == ".|.")
159     {
160       m.add_at_edge (X_AXIS, LEFT, thick, thinkern);
161       m.add_at_edge (X_AXIS, RIGHT, thick, kern);      
162     }
163   else if (str == "||")
164     {
165       m.add_at_edge (X_AXIS, RIGHT, thin, 0);
166       m.add_at_edge (X_AXIS, RIGHT, thin, thinkern);      
167     }
168
169   return m;
170 }
171
172 Molecule 
173 Lookup::beam (Real slope, Real width, Real thick) 
174 {
175   Real height = slope * width; 
176   Real min_y = (0 <? height) - thick/2;
177   Real max_y = (0 >? height) + thick/2;
178
179   
180   Molecule m;
181   m.dim_[X_AXIS] = Interval (0, width);
182   m.dim_[Y_AXIS] = Interval (min_y, max_y);
183
184   
185   Atom *at = new Atom
186     (gh_list (ly_symbol2scm ("beam"),
187               gh_double2scm (width),
188               gh_double2scm (slope),
189               gh_double2scm (thick),
190               SCM_UNDEFINED));
191
192   m.add_atom (at->self_scm_);
193   return m;
194 }
195
196
197
198 Molecule
199 Lookup::dashed_slur (Bezier b, Real thick, Real dash)
200 {
201   SCM l = SCM_EOL;
202   for (int i= 4; i -- ;)
203     {
204       l = gh_cons (ly_offset2scm (b.control_[i]), l);
205     }
206
207   Atom *at = new Atom(gh_list (ly_symbol2scm ("dashed-slur"),
208                                gh_double2scm (thick), 
209                                gh_double2scm (dash),
210                                ly_quote_scm (l),
211                                SCM_UNDEFINED));
212   Molecule m;
213   m.add_atom (at->self_scm_);
214   return m;
215 }
216
217
218
219
220 Molecule
221 Lookup::fill (Box b) 
222 {
223   Molecule m;
224   m.dim_ = b;
225   return m;
226 }
227
228
229 Molecule
230 Lookup::filledbox (Box b ) 
231 {
232   Molecule m;
233   
234   Atom* at  = new Atom(gh_list (ly_symbol2scm ("filledbox"),
235                      gh_double2scm (-b[X_AXIS][LEFT]),
236                      gh_double2scm (b[X_AXIS][RIGHT]),                 
237                      gh_double2scm (-b[Y_AXIS][DOWN]),
238                      gh_double2scm (b[Y_AXIS][UP]),                    
239                      SCM_UNDEFINED));
240
241   m.dim_ = b;
242   m.add_atom (at->self_scm_);
243   return m;
244 }
245
246 Molecule
247 Lookup::frame (Box b, Real thick)
248 {
249   Molecule m;
250   Direction d = LEFT;
251   Axis a = X_AXIS;
252   while (a < NO_AXES)
253     {
254       do
255         {
256           Axis o = Axis ((a+1)%NO_AXES);
257
258           Box edges;
259           edges[a] = b[a][d] + 0.5 * thick * Interval (-1, 1);
260           edges[o][DOWN] = b[o][DOWN] - thick/2;
261           edges[o][UP] = b[o][UP] + thick/2;      
262           
263           
264           m.add_molecule (filledbox (edges));
265         }
266       while (flip (&d) != LEFT);
267     }
268   return m;
269   
270 }
271
272
273 /*
274    TODO: THIS IS UGLY.  Since the user has direct access to TeX
275    strings, we try some halfbaked attempt to detect TeX trickery.
276  */
277 String
278 sanitise_TeX_string (String text)
279 {
280   int brace_count =0;
281   for (int i= 0; i < text.length_i (); i++)
282     {
283       if (text[i] == '\\')
284         continue;
285       
286       if (text[i] == '{')
287         brace_count ++;
288       else if (text[i] == '}')
289         brace_count --;
290     }
291   
292   if(brace_count)
293     {
294       warning (_f ("Non-matching braces in text `%s', adding braces", text.ch_C()));
295
296       if (brace_count < 0)
297         {
298           text = to_str ('{', -brace_count) + text;
299         }
300       else 
301         {
302           text = text + to_str ('}', brace_count);
303         }
304     }
305     
306   return text;
307 }
308
309 /**
310    TODO!
311  */
312 String
313 sanitise_PS_string (String t)
314 {
315   return t;
316 }
317
318 /**
319
320 */
321 Molecule
322 Lookup::text (String style, String text, Paper_def *paper_l) 
323 {
324   Molecule m;
325   if (style.empty_b ())
326     style = "roman";
327   
328   int font_mag = 0;
329   Real font_h = paper_l->get_var ("font_normal");
330   if (paper_l->scope_p_->elem_b ("font_" + style))
331     {
332       font_h = paper_l->get_var ("font_" + style);
333     }
334
335
336   if (paper_l->scope_p_->elem_b ("magnification_" + style))
337     {
338       font_mag = (int)paper_l->get_var ("magnification_" + style);
339     }
340
341   /*
342     UGH.
343   */
344   SCM l = ly_eval_str (("(style-to-cmr \"" + style + "\")").ch_C());
345   if (l != SCM_BOOL_F)
346     {
347       style = ly_scm2string (gh_cdr(l)) +to_str  ((int)font_h);
348     }
349
350   
351
352   Font_metric* metric_l = 0;
353
354   if (font_mag)
355     metric_l = all_fonts_global_p->find_scaled (style, font_mag);
356   else
357     metric_l = all_fonts_global_p->find_font (style);
358   
359   
360   if (output_global_ch == "tex")
361     text = sanitise_TeX_string  (text);
362   else if (output_global_ch == "ps")
363     text = sanitise_PS_string (text);
364     
365   m.dim_ = metric_l->text_dimension (text);
366   
367   Atom *at = new Atom (gh_list (ly_symbol2scm ("text"),
368                      ly_str02scm (text.ch_C()),
369                      SCM_UNDEFINED));
370   at->fontify (metric_l);
371   
372   m.add_atom (at->self_scm_);
373   return m;
374 }
375   
376
377
378 Molecule
379 Lookup::staff_brace (Real y, int staff_size) 
380 {
381   Molecule m;
382
383   // URG
384   Real step  = 1.0;
385   int minht  = 2 * staff_size;
386   int maxht = 7 *  minht;
387   int idx = int (((maxht - step) <? y - minht) / step);
388   idx = idx >? 0;
389
390   SCM l = ly_eval_str ("(style-to-cmr \"brace\")");
391   String nm = "feta-braces";
392   if (l != SCM_BOOL_F)
393     nm = ly_scm2string (gh_cdr (l));
394   nm += to_str (staff_size);
395   SCM e =gh_list (ly_symbol2scm ("char"), gh_int2scm (idx), SCM_UNDEFINED);
396   Atom *at = new Atom (e);
397
398   at->fontify (all_fonts_global_p->find_font (nm));
399   
400   m.dim_[Y_AXIS] = Interval (-y/2,y/2);
401   m.dim_[X_AXIS] = Interval (0,0);
402   m.add_atom (at->self_scm_);
403   return m;
404 }
405  
406
407 /*
408   Make a smooth curve along the points 
409  */
410 Molecule
411 Lookup::slur (Bezier curve, Real curvethick, Real linethick) 
412 {
413   Real alpha = (curve.control_[3] - curve.control_[0]).arg ();
414   Bezier back = curve;
415
416   back.reverse ();
417   back.control_[1] += curvethick * complex_exp (Offset (0, alpha + M_PI/2));
418   back.control_[2] += curvethick * complex_exp (Offset (0, alpha + M_PI/2));  
419
420   SCM scontrols[8];
421   for (int i=4; i--;)
422     scontrols[ i ] = ly_offset2scm (back.control_[i]);
423   for (int i=4 ; i--;)
424     scontrols[i+4] = ly_offset2scm (curve.control_[i]);
425
426   /*
427     Need the weird order b.o. the way PS want its arguments  
428    */
429   int indices[]= {5, 6, 7, 4, 1, 2, 3, 0};
430   SCM list = SCM_EOL;
431   for (int i= 8; i--;  )
432     {
433       list = gh_cons (scontrols[indices[i]], list);
434     }
435   
436   
437   Atom *at = new Atom (gh_list (ly_symbol2scm ("bezier-sandwich"),
438                      ly_quote_scm (list),
439                      gh_double2scm (linethick),
440                      SCM_UNDEFINED));
441
442   Molecule m; 
443   m.dim_[X_AXIS] = curve.extent (X_AXIS);
444   m.dim_[Y_AXIS] = curve.extent (Y_AXIS);
445   m.add_atom (at->self_scm_);
446   return m;
447 }
448
449 Molecule
450 Lookup::staff_bracket (Real height, Paper_def* paper_l)
451 {
452   Molecule m;
453   Atom *at = new Atom  ( gh_list (ly_symbol2scm ("bracket"),
454                       gh_double2scm (paper_l->get_var("bracket_arch_angle")),
455                       gh_double2scm (paper_l->get_var("bracket_arch_width")),
456                       gh_double2scm (paper_l->get_var("bracket_arch_height")),
457                       gh_double2scm (paper_l->get_var("bracket_width")),
458                       gh_double2scm (height),
459                       gh_double2scm (paper_l->get_var("bracket_arch_thick")),
460                       gh_double2scm (paper_l->get_var("bracket_thick")),
461                       SCM_UNDEFINED));
462   
463   m.add_atom (at->self_scm_);                            
464   m.dim_[Y_AXIS] = Interval (-height/2,height/2);
465   m.dim_[X_AXIS] = Interval (0,4 PT);
466
467   m.translate_axis (- 4. / 3. * m.dim_[X_AXIS].length (), X_AXIS);
468   return m;
469 }
470
471
472 Molecule
473 Lookup::accordion (SCM s, Real staff_space) const
474 {
475   Molecule m;
476   String sym = ly_scm2string(gh_car (s));
477   String reg = ly_scm2string(gh_car (gh_cdr(s)));
478
479   if (sym == "Discant")
480     {
481       Molecule r = afm_find("scripts-accDiscant");
482       m.add_molecule(r);
483       if (reg.left_str(1) == "F")
484         {
485           Molecule d = afm_find("scripts-accDot");
486           d.translate_axis(staff_space * 2.5 PT, Y_AXIS);
487           m.add_molecule(d);
488           reg = reg.right_str(reg.length_i()-1);
489         }
490       int eflag = 0x00;
491       if (reg.left_str(3) == "EEE")
492         {
493           eflag = 0x07;
494           reg = reg.right_str(reg.length_i()-3);
495         }
496       else if (reg.left_str(2) == "EE")
497         {
498           eflag = 0x05;
499           reg = reg.right_str(reg.length_i()-2);
500         }
501       else if (reg.left_str(2) == "Eh")
502         {
503           eflag = 0x04;
504           reg = reg.right_str(reg.length_i()-2);
505         }
506       else if (reg.left_str(1) == "E")
507         {
508           eflag = 0x02;
509           reg = reg.right_str(reg.length_i()-1);
510         }
511       if (eflag & 0x02)
512         {
513           Molecule d = afm_find("scripts-accDot");
514           d.translate_axis(staff_space * 1.5 PT, Y_AXIS);
515           m.add_molecule(d);
516         }
517       if (eflag & 0x04)
518         {
519           Molecule d = afm_find("scripts-accDot");
520           d.translate_axis(staff_space * 1.5 PT, Y_AXIS);
521           d.translate_axis(0.8 * staff_space PT, X_AXIS);
522           m.add_molecule(d);
523         }
524       if (eflag & 0x01)
525         {
526           Molecule d = afm_find("scripts-accDot");
527           d.translate_axis(staff_space * 1.5 PT, Y_AXIS);
528           d.translate_axis(-0.8 * staff_space PT, X_AXIS);
529           m.add_molecule(d);
530         }
531       if (reg.left_str(2) == "SS")
532         {
533           Molecule d = afm_find("scripts-accDot");
534           d.translate_axis(0.5 * staff_space PT, Y_AXIS);
535           d.translate_axis(0.4 * staff_space PT, X_AXIS);
536           m.add_molecule(d);
537           d.translate_axis(-0.8 * staff_space PT, X_AXIS);
538           m.add_molecule(d);
539           reg = reg.right_str(reg.length_i()-2);
540         }
541       if (reg.left_str(1) == "S")
542         {
543           Molecule d = afm_find("scripts-accDot");
544           d.translate_axis(0.5 * staff_space PT, Y_AXIS);
545           m.add_molecule(d);
546           reg = reg.right_str(reg.length_i()-1);
547         }
548     }
549   else if (sym == "Freebase")
550     {
551       Molecule r = afm_find("scripts-accFreebase");
552       m.add_molecule(r);
553       if (reg.left_str(1) == "F")
554         {
555           Molecule d = afm_find("scripts-accDot");
556           d.translate_axis(staff_space * 1.5 PT, Y_AXIS);
557           m.add_molecule(d);
558           reg = reg.right_str(reg.length_i()-1);
559         }
560       if (reg == "E")
561         {
562           Molecule d = afm_find("scripts-accDot");
563           d.translate_axis(staff_space * 0.5 PT, Y_AXIS);
564           m.add_molecule(d);
565         }
566     }
567   else if (sym == "Bayanbase")
568     {
569       Molecule r = afm_find("scripts-accBayanbase");
570       m.add_molecule(r);
571       if (reg.left_str(1) == "T")
572         {
573           Molecule d = afm_find("scripts-accDot");
574           d.translate_axis(staff_space * 2.5 PT, Y_AXIS);
575           m.add_molecule(d);
576           reg = reg.right_str(reg.length_i()-1);
577         }
578       /* include 4' reed just for completeness. You don't want to use this. */
579       if (reg.left_str(1) == "F")
580         {
581           Molecule d = afm_find("scripts-accDot");
582           d.translate_axis(staff_space * 1.5 PT, Y_AXIS);
583           m.add_molecule(d);
584           reg = reg.right_str(reg.length_i()-1);
585         }
586       if (reg.left_str(2) == "EE")
587         {
588           Molecule d = afm_find("scripts-accDot");
589           d.translate_axis(staff_space * 0.5 PT, Y_AXIS);
590           d.translate_axis(0.4 * staff_space PT, X_AXIS);
591           m.add_molecule(d);
592           d.translate_axis(-0.8 * staff_space PT, X_AXIS);
593           m.add_molecule(d);
594           reg = reg.right_str(reg.length_i()-2);
595         }
596       if (reg.left_str(1) == "E")
597         {
598           Molecule d = afm_find("scripts-accDot");
599           d.translate_axis(staff_space * 0.5 PT, Y_AXIS);
600           m.add_molecule(d);
601           reg = reg.right_str(reg.length_i()-1);
602         }
603     }
604   else if (sym == "Stdbase")
605     {
606       Molecule r = afm_find("scripts-accStdbase");
607       m.add_molecule(r);
608       if (reg.left_str(1) == "T")
609         {
610           Molecule d = afm_find("scripts-accDot");
611           d.translate_axis(staff_space * 3.5 PT, Y_AXIS);
612           m.add_molecule(d);
613           reg = reg.right_str(reg.length_i()-1);
614         }
615       if (reg.left_str(1) == "F")
616         {
617           Molecule d = afm_find("scripts-accDot");
618           d.translate_axis(staff_space * 2.5 PT, Y_AXIS);
619           m.add_molecule(d);
620           reg = reg.right_str(reg.length_i()-1);
621         }
622       if (reg.left_str(1) == "M")
623         {
624           Molecule d = afm_find("scripts-accDot");
625           d.translate_axis(staff_space * 2 PT, Y_AXIS);
626           d.translate_axis(staff_space PT, X_AXIS);
627           m.add_molecule(d);
628           reg = reg.right_str(reg.length_i()-1);
629         }
630       if (reg.left_str(1) == "E")
631         {
632           Molecule d = afm_find("scripts-accDot");
633           d.translate_axis(staff_space * 1.5 PT, Y_AXIS);
634           m.add_molecule(d);
635           reg = reg.right_str(reg.length_i()-1);
636         }
637       if (reg.left_str(1) == "S")
638         {
639           Molecule d = afm_find("scripts-accDot");
640           d.translate_axis(staff_space * 0.5 PT, Y_AXIS);
641           m.add_molecule(d);
642           reg = reg.right_str(reg.length_i()-1);
643         }
644     }
645   /* ugh maybe try to use regular font for S.B. and B.B and only use one font
646      for the rectangle */
647   else if (sym == "SB")
648     {
649       Molecule r = afm_find("scripts-accSB");
650       m.add_molecule(r);
651     }
652   else if (sym == "BB")
653     {
654       Molecule r = afm_find("scripts-accBB");
655       m.add_molecule(r);
656     }
657   else if (sym == "OldEE")
658     {
659       Molecule r = afm_find("scripts-accOldEE");
660       m.add_molecule(r);
661     }
662   else if (sym == "OldEES")
663     {
664       Molecule r = afm_find("scripts-accOldEES");
665       m.add_molecule(r);
666     }
667   return m;  
668 }
669