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