]> git.donarmstrong.com Git - lilypond.git/blob - lily/lookup.cc
* buildscripts/lilypond-profile.sh: error message if script is not
[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--2003 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
16 #include "warn.hh"
17 #include "dimensions.hh"
18 #include "bezier.hh"
19 #include "string-convert.hh"
20 #include "file-path.hh"
21 #include "main.hh"
22 #include "lily-guile.hh"
23 #include "molecule.hh"
24 #include "lookup.hh"
25 #include "font-metric.hh"
26 #include "interval.hh"
27
28 Molecule
29 Lookup::dot (Offset p, Real radius)
30 {
31   SCM at = (scm_list_n (ly_symbol2scm ("dot"),
32                         gh_double2scm (p[X_AXIS]),
33                         gh_double2scm (p[Y_AXIS]),
34                         gh_double2scm (radius),
35                         SCM_UNDEFINED));
36   Box box;
37   box.add_point (p - Offset (radius, radius));
38   box.add_point (p + Offset (radius, radius));
39   return Molecule (box, at);
40 }
41
42 Molecule 
43 Lookup::beam (Real slope, Real width, Real thick) 
44 {
45   Real height = slope * width; 
46   Real min_y = (0 <? height) - thick/2;
47   Real max_y = (0 >? height) + thick/2;
48
49   
50
51   Box b (Interval (0, width),
52          Interval (min_y, max_y));
53
54   
55   SCM at = scm_list_n (ly_symbol2scm ("beam"),
56                     gh_double2scm (width),
57                     gh_double2scm (slope),
58                     gh_double2scm (thick),
59                     SCM_UNDEFINED);
60   return Molecule (b, at);
61 }
62
63 Molecule
64 Lookup::dashed_slur (Bezier b, Real thick, Real dash)
65 {
66   SCM l = SCM_EOL;
67
68   for (int i= 4; i -- ;)
69     {
70       l = gh_cons (ly_offset2scm (b.control_[i]), l);
71     }
72
73   SCM at = (scm_list_n (ly_symbol2scm ("dashed-slur"),
74                                gh_double2scm (thick), 
75                                gh_double2scm (dash),
76                                ly_quote_scm (l),
77                                SCM_UNDEFINED));
78
79   Box box (Interval (0,0),Interval (0,0));
80   return   Molecule (box, at);
81 }
82
83 Molecule
84 Lookup::line (Real th, Offset from, Offset to)
85 {
86   SCM at = scm_list_n (ly_symbol2scm ("draw-line"),
87                         gh_double2scm (th), 
88                         gh_double2scm (from[X_AXIS]),
89                         gh_double2scm (from[Y_AXIS]),
90                         gh_double2scm (to[X_AXIS]),
91                         gh_double2scm (to[Y_AXIS]),
92                         SCM_UNDEFINED);
93
94   Box box;
95   box.add_point (from);
96   box.add_point (to);
97
98   box[X_AXIS].widen (th/2);
99   box[Y_AXIS].widen (th/2);  
100
101   return Molecule (box, at);
102 }
103
104 Molecule
105 Lookup::horizontal_line (Interval w, Real th)
106 {
107   SCM at = scm_list_n (ly_symbol2scm ("horizontal-line"),
108                        gh_double2scm (w[LEFT]), 
109                        gh_double2scm (w[RIGHT]),
110                        gh_double2scm (th),
111                        SCM_UNDEFINED);
112
113
114   Box box ;
115   box[X_AXIS] = w;
116   box[Y_AXIS] = Interval (-th/2,th/2);
117
118   return Molecule (box, at);
119 }
120
121
122 Molecule
123 Lookup::blank (Box b) 
124 {
125   return Molecule (b, scm_makfrom0str (""));
126 }
127
128 Molecule
129 Lookup::filledbox (Box b) 
130 {
131   SCM  at  = (scm_list_n (ly_symbol2scm ("filledbox"),
132                      gh_double2scm (-b[X_AXIS][LEFT]),
133                      gh_double2scm (b[X_AXIS][RIGHT]),                 
134                      gh_double2scm (-b[Y_AXIS][DOWN]),
135                      gh_double2scm (b[Y_AXIS][UP]),                    
136                      SCM_UNDEFINED));
137
138   return Molecule (b,at);
139 }
140
141 /*
142  * round filled box:
143  *
144  *   __________________________________
145  *  /     \  ^           /     \      ^
146  * |         |blot              |     |
147  * |       | |dia       |       |     |
148  * |         |meter             |     |
149  * |\ _ _ /  v           \ _ _ /|     |
150  * |                            |     |
151  * |                            |     | Box
152  * |                    <------>|     | extent
153  * |                      blot  |     | (Y_AXIS)
154  * |                    diameter|     |
155  * |                            |     |
156  * |  _ _                  _ _  |     |
157  * |/     \              /     \|     |
158  * |                            |     |
159  * |       |            |       |     |
160  * |                            |     |
161  * x\_____/______________\_____/|_____v
162  * |(0,0)                       |
163  * |                            |
164  * |                            |
165  * |<-------------------------->|
166  *       Box extent(X_AXIS)
167  */
168 Molecule
169 Lookup::roundfilledbox (Box b, Real blotdiameter)
170 {
171   if (b.x ().length () < blotdiameter)
172     {
173       programming_error (_f ("round filled box horizontal extent smaller than blot; decreasing blot"));
174       blotdiameter = b.x ().length ();
175     }
176   if (b.y ().length () < blotdiameter)
177     {
178       programming_error (_f ("round filled box vertical extent smaller than blot; decreasing blot"));
179       blotdiameter = b.y ().length ();
180     }
181
182   SCM at = (scm_list_n (ly_symbol2scm ("roundfilledbox"),
183                         gh_double2scm (-b[X_AXIS][LEFT]),
184                         gh_double2scm (b[X_AXIS][RIGHT]),
185                         gh_double2scm (-b[Y_AXIS][DOWN]),
186                         gh_double2scm (b[Y_AXIS][UP]),
187                         gh_double2scm (blotdiameter),
188                         SCM_UNDEFINED));
189
190   return Molecule (b,at);
191 }
192
193 LY_DEFINE(ly_round_filled_box, "ly:round-filled-box",
194           2, 0,0,
195           (SCM b, SCM blot),
196           "Make a box with rounded corners. B is a pair of number-pairs, and BLOT a number")
197 {
198   SCM_ASSERT_TYPE(gh_number_p (blot), blot, SCM_ARG2, __FUNCTION__, "number") ;
199   SCM_ASSERT_TYPE(gh_pair_p (b), b, SCM_ARG4, __FUNCTION__, "pair") ;
200
201   SCM_ASSERT_TYPE(ly_number_pair_p (gh_car (b)), gh_car (b), SCM_ARG1, __FUNCTION__, "number-pair") ;
202   SCM_ASSERT_TYPE(ly_number_pair_p (gh_cdr (b)), gh_cdr (b), SCM_ARG1, __FUNCTION__, "number-pair") ;
203
204   Interval x (ly_scm2interval (gh_car (b)));
205   Interval y (ly_scm2interval (gh_cdr (b)));
206   
207   return Lookup::roundfilledbox (Box (x,y),
208                                  gh_scm2double (blot)).smobbed_copy();
209   
210 }
211           
212
213 /*
214  * Create Molecule that represents a filled polygon with round edges.
215  *
216  * LIMITATIONS:
217  *
218  * (a) Only outer (convex) edges are rounded.
219  *
220  * (b) This algorithm works as expected only for polygons whose edges
221  * do not intersect.  For example, the polygon ((0, 0), (q, 0), (0,
222  * q), (q, q)) has an intersection at point (q/2, q/2) and therefore
223  * will give a strange result.  Even non-adjacent edges that just
224  * touch each other will in general not work as expected for non-null
225  * blotdiameter.
226  *
227  * (c) Given a polygon ((x0, y0), (x1, y1), ... , (x(n-1), y(n-1))),
228  * if there is a natural number k such that blotdiameter is greater
229  * than the maximum of { | (x(k mod n), y(k mod n)) - (x((k+1) mod n),
230  * y((k+1) mod n)) |, | (x(k mod n), y(k mod n)) - (x((k+2) mod n),
231  * y((k+2) mod n)) |, | (x((k+1) mod n), y((k+1) mod n)) - (x((k+2)
232  * mod n), y((k+2) mod n)) | }, then the outline of the rounded
233  * polygon will exceed the outline of the core polygon.  In other
234  * words: Do not draw rounded polygons that have a leg smaller or
235  * thinner than blotdiameter (or set blotdiameter to a sufficiently
236  * small value -- maybe even 0.0)!
237  *
238  * NOTE: Limitations (b) and (c) arise from the fact that round edges
239  * are made by moulding sharp edges to round ones rather than adding
240  * to a core filled polygon.  For details of these two different
241  * approaches, see the thread upon the ledger lines patch that started
242  * on March 25, 2002 on the devel mailing list.  The below version of
243  * round_filled_polygon() sticks to the moulding model, which the
244  * majority of the list participants finally voted for.  This,
245  * however, results in the above limitations and a much increased
246  * complexity of the algorithm, since it has to compute a shrinked
247  * polygon -- which is not trivial define precisely and unambigously.
248  * With the other approach, one simply could move a circle of size
249  * blotdiameter along all edges of the polygon (which is what the
250  * postscript routine in the backend effectively does, but on the
251  * shrinked polygon). --jr
252  */
253 Molecule
254 Lookup::round_filled_polygon (Array<Offset> points, Real blotdiameter)
255 {
256   /* TODO: Maybe print a warning if one of the above limitations
257      applies to the given polygon.  However, this is quite complicated
258      to check. */
259
260   /* remove consecutive duplicate points */
261   const Real epsilon = 0.01;
262   for (int i = 0; i < points.size ();)
263     {
264       int next_i = (i + 1) % points.size ();
265       Real d = (points[i] - points[next_i]).length ();
266       if (d < epsilon)
267         points.del (next_i);
268       else
269         i++;
270     }
271
272   /* special cases: degenerated polygons */
273   if (points.size () == 0)
274     return Molecule ();
275   if (points.size () == 1)
276     return dot (points[0], 0.5 * blotdiameter);
277   if (points.size () == 2)
278     return line (blotdiameter, points[0], points[1]);
279
280   /* shrink polygon in size by 0.5 * blotdiameter */
281   Array<Offset> shrinked_points;
282   shrinked_points.set_size (points.size ());
283   bool ccw = 1; // true, if three adjacent points are counterclockwise ordered
284   for (int i = 0; i < points.size (); i++)
285     {
286       int i0 = i;
287       int i1 = (i + 1) % points.size ();
288       int i2 = (i + 2) % points.size ();
289       Offset p0 = points[i0];
290       Offset p1 = points[i1];
291       Offset p2 = points[i2];
292       Offset p10 = p0 - p1;
293       Offset p12 = p2 - p1;
294       if (p10.length () != 0.0)
295         { // recompute ccw
296           Real phi = p10.arg ();
297           // rotate (p2 - p0) by (-phi)
298           Offset q = complex_multiply (p2 - p0, complex_exp (Offset (1.0, -phi)));
299
300           if (q[Y_AXIS] > 0)
301             ccw = 1;
302           else if (q[Y_AXIS] < 0)
303             ccw = 0;
304           else {} // keep ccw unchanged
305         }
306       else {} // keep ccw unchanged
307       Offset p10n = (1.0 / p10.length ()) * p10; // normalize length to 1.0
308       Offset p12n = (1.0 / p12.length ()) * p12;
309       Offset p13n = 0.5 * (p10n + p12n);
310       Offset p14n = 0.5 * (p10n - p12n);
311       Offset p13;
312       Real d = p13n.length () * p14n.length (); // distance p3n to line(p1..p0)
313       if (d < epsilon)
314         // special case: p0, p1, p2 are on a single line => build
315         // vector orthogonal to (p2-p0) of length 0.5 blotdiameter
316         {
317           p13[X_AXIS] = p10[Y_AXIS];
318           p13[Y_AXIS] = -p10[X_AXIS];
319           p13 = (0.5 * blotdiameter / p13.length ()) * p13;
320         }
321       else
322         p13 = (0.5 * blotdiameter / d) * p13n;
323       shrinked_points[i1] = p1 + ((ccw) ? p13 : -p13);
324     }
325
326   /* build scm expression and bounding box */
327   SCM shrinked_points_scm = SCM_EOL;
328   Box box;
329   for (int i = 0; i < shrinked_points.size (); i++)
330     {
331       SCM x = gh_double2scm (shrinked_points[i][X_AXIS]);
332       SCM y = gh_double2scm (shrinked_points[i][Y_AXIS]);
333       shrinked_points_scm = gh_cons (x, gh_cons (y, shrinked_points_scm));
334       box.add_point (points[i]);
335     }
336   SCM polygon_scm = scm_list_n (ly_symbol2scm ("polygon"),
337                                 ly_quote_scm (shrinked_points_scm),
338                                 gh_double2scm (blotdiameter),
339                                 SCM_UNDEFINED);
340
341   Molecule polygon = Molecule (box, polygon_scm);
342   shrinked_points.clear ();
343   return polygon;
344 }
345
346 Molecule
347 Lookup::frame (Box b, Real thick)
348 {
349   Molecule m;
350   Direction d = LEFT;
351   for (Axis a = X_AXIS; a < NO_AXES; a = Axis (a + 1))
352     {
353       Axis o = Axis ((a+1)%NO_AXES);
354       do
355         {
356           Box edges;
357           edges[a] = b[a][d] + 0.5 * thick * Interval (-1, 1);
358           edges[o][DOWN] = b[o][DOWN] - thick/2;
359           edges[o][UP] = b[o][UP] + thick/2;      
360           
361           m.add_molecule (filledbox (edges));
362         }
363       while (flip (&d) != LEFT);
364     }
365   return m;
366   
367 }
368
369 /*
370   Make a smooth curve along the points 
371  */
372 Molecule
373 Lookup::slur (Bezier curve, Real curvethick, Real linethick) 
374 {
375   Real alpha = (curve.control_[3] - curve.control_[0]).arg ();
376   Bezier back = curve;
377   Offset perp = curvethick * complex_exp (Offset (0, alpha + M_PI/2)) * 0.5;
378   back.reverse ();
379   back.control_[1] += perp;
380   back.control_[2] += perp;
381
382   curve.control_[1] -= perp;
383   curve.control_[2] -= perp;
384   
385   SCM scontrols[8];
386
387   for (int i=4; i--;)
388     scontrols[ i ] = ly_offset2scm (back.control_[i]);
389   for (int i=4 ; i--;)
390     scontrols[i+4] = ly_offset2scm (curve.control_[i]);
391
392   /*
393     Need the weird order b.o. the way PS want its arguments  
394    */
395   int indices[]= {5, 6, 7, 4, 1, 2, 3, 0};
396   SCM list = SCM_EOL;
397   for (int i= 8; i--;)
398     {
399       list = gh_cons (scontrols[indices[i]], list);
400     }
401   
402   
403   SCM at = (scm_list_n (ly_symbol2scm ("bezier-bow"),
404                      ly_quote_scm (list),
405                      gh_double2scm (linethick),
406                      SCM_UNDEFINED));
407   Box b(curve.extent (X_AXIS),
408         curve.extent (Y_AXIS));
409
410   b[X_AXIS].unite (back.extent (X_AXIS));
411   b[Y_AXIS].unite (back.extent (Y_AXIS));
412
413   return Molecule (b, at);
414 }
415
416 /*
417  * Bezier Sandwich:
418  *
419  *                               .|
420  *                        .       |
421  *              top .             |
422  *              . curve           |
423  *          .                     |
424  *       .                        |
425  *     .                          |
426  *    |                           |
427  *    |                          .|
428  *    |                     .
429  *    |         bottom .
430  *    |            . curve
431  *    |         .
432  *    |      .
433  *    |   .
434  *    | .
435  *    |.
436  *    |
437  *
438  */
439 Molecule
440 Lookup::bezier_sandwich (Bezier top_curve, Bezier bottom_curve)
441 {
442   /*
443     Need the weird order b.o. the way PS want its arguments  
444    */
445   SCM list = SCM_EOL;
446   list = gh_cons (ly_offset2scm (bottom_curve.control_[3]), list);
447   list = gh_cons (ly_offset2scm (bottom_curve.control_[0]), list);
448   list = gh_cons (ly_offset2scm (bottom_curve.control_[1]), list);
449   list = gh_cons (ly_offset2scm (bottom_curve.control_[2]), list);
450   list = gh_cons (ly_offset2scm (top_curve.control_[0]), list);
451   list = gh_cons (ly_offset2scm (top_curve.control_[3]), list);
452   list = gh_cons (ly_offset2scm (top_curve.control_[2]), list);
453   list = gh_cons (ly_offset2scm (top_curve.control_[1]), list);
454
455   SCM horizontal_bend = scm_list_n (ly_symbol2scm ("bezier-sandwich"),
456                                     ly_quote_scm (list),
457                                     gh_double2scm (0.0),
458                                     SCM_UNDEFINED);
459
460   Interval x_extent = top_curve.extent (X_AXIS);
461   x_extent.unite (bottom_curve.extent (X_AXIS));
462   Interval y_extent = top_curve.extent (Y_AXIS);
463   y_extent.unite (bottom_curve.extent (Y_AXIS));
464   Box b (x_extent, y_extent);
465
466   return Molecule (b, horizontal_bend);
467 }
468
469 /*
470  * Horizontal Slope:
471  *
472  *            /|   ^
473  *           / |   |
474  *          /  |   | height
475  *         /   |   |
476  *        /    |   v
477  *       |    /
478  *       |   /
479  * (0,0) x  /slope=dy/dx
480  *       | /
481  *       |/
482  *
483  *       <----->
484  *        width
485  */
486 Molecule
487 Lookup::horizontal_slope (Real width, Real slope, Real height)
488 {
489   SCM width_scm = gh_double2scm (width);
490   SCM slope_scm = gh_double2scm (slope);
491   SCM height_scm = gh_double2scm (height);
492   SCM horizontal_slope = scm_list_n (ly_symbol2scm ("beam"),
493                                      width_scm, slope_scm,
494                                      height_scm, SCM_UNDEFINED);
495   Box b (Interval (0, width),
496          Interval (-height/2, height/2 + width*slope));
497   return Molecule (b, horizontal_slope);
498 }
499
500 /*
501   TODO: junk me.
502  */
503 Molecule
504 Lookup::accordion (SCM s, Real staff_space, Font_metric *fm) 
505 {
506   Molecule m;
507   String sym = ly_scm2string (ly_car (s));
508   String reg = ly_scm2string (ly_car (ly_cdr (s)));
509
510   if (sym == "Discant")
511     {
512       Molecule r = fm->find_by_name ("accordion-accDiscant");
513       m.add_molecule (r);
514       if (reg.left_string (1) == "F")
515         {
516           Molecule d = fm->find_by_name ("accordion-accDot");
517           d.translate_axis (staff_space * 2.5 PT, Y_AXIS);
518           m.add_molecule (d);
519           reg = reg.right_string (reg.length ()-1);
520         }
521       int eflag = 0x00;
522       if (reg.left_string (3) == "EEE")
523         {
524           eflag = 0x07;
525           reg = reg.right_string (reg.length ()-3);
526         }
527       else if (reg.left_string (2) == "EE")
528         {
529           eflag = 0x05;
530           reg = reg.right_string (reg.length ()-2);
531         }
532       else if (reg.left_string (2) == "Eh")
533         {
534           eflag = 0x04;
535           reg = reg.right_string (reg.length ()-2);
536         }
537       else if (reg.left_string (1) == "E")
538         {
539           eflag = 0x02;
540           reg = reg.right_string (reg.length ()-1);
541         }
542       if (eflag & 0x02)
543         {
544           Molecule d = fm->find_by_name ("accordion-accDot");
545           d.translate_axis (staff_space * 1.5 PT, Y_AXIS);
546           m.add_molecule (d);
547         }
548       if (eflag & 0x04)
549         {
550           Molecule d = fm->find_by_name ("accordion-accDot");
551           d.translate_axis (staff_space * 1.5 PT, Y_AXIS);
552           d.translate_axis (0.8 * staff_space PT, X_AXIS);
553           m.add_molecule (d);
554         }
555       if (eflag & 0x01)
556         {
557           Molecule d = fm->find_by_name ("accordion-accDot");
558           d.translate_axis (staff_space * 1.5 PT, Y_AXIS);
559           d.translate_axis (-0.8 * staff_space PT, X_AXIS);
560           m.add_molecule (d);
561         }
562       if (reg.left_string (2) == "SS")
563         {
564           Molecule d = fm->find_by_name ("accordion-accDot");
565           d.translate_axis (0.5 * staff_space PT, Y_AXIS);
566           d.translate_axis (0.4 * staff_space PT, X_AXIS);
567           m.add_molecule (d);
568           d.translate_axis (-0.8 * staff_space PT, X_AXIS);
569           m.add_molecule (d);
570           reg = reg.right_string (reg.length ()-2);
571         }
572       if (reg.left_string (1) == "S")
573         {
574           Molecule d = fm->find_by_name ("accordion-accDot");
575           d.translate_axis (0.5 * staff_space PT, Y_AXIS);
576           m.add_molecule (d);
577           reg = reg.right_string (reg.length ()-1);
578         }
579     }
580   else if (sym == "Freebase")
581     {
582       Molecule r = fm->find_by_name ("accordion-accFreebase");
583       m.add_molecule (r);
584       if (reg.left_string (1) == "F")
585         {
586           Molecule d = fm->find_by_name ("accordion-accDot");
587           d.translate_axis (staff_space * 1.5 PT, Y_AXIS);
588           m.add_molecule (d);
589           reg = reg.right_string (reg.length ()-1);
590         }
591       if (reg == "E")
592         {
593           Molecule d = fm->find_by_name ("accordion-accDot");
594           d.translate_axis (staff_space * 0.5 PT, Y_AXIS);
595           m.add_molecule (d);
596         }
597     }
598   else if (sym == "Bayanbase")
599     {
600       Molecule r = fm->find_by_name ("accordion-accBayanbase");
601       m.add_molecule (r);
602       if (reg.left_string (1) == "T")
603         {
604           Molecule d = fm->find_by_name ("accordion-accDot");
605           d.translate_axis (staff_space * 2.5 PT, Y_AXIS);
606           m.add_molecule (d);
607           reg = reg.right_string (reg.length ()-1);
608         }
609       /* include 4' reed just for completeness. You don't want to use this. */
610       if (reg.left_string (1) == "F")
611         {
612           Molecule d = fm->find_by_name ("accordion-accDot");
613           d.translate_axis (staff_space * 1.5 PT, Y_AXIS);
614           m.add_molecule (d);
615           reg = reg.right_string (reg.length ()-1);
616         }
617       if (reg.left_string (2) == "EE")
618         {
619           Molecule d = fm->find_by_name ("accordion-accDot");
620           d.translate_axis (staff_space * 0.5 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_string (reg.length ()-2);
626         }
627       if (reg.left_string (1) == "E")
628         {
629           Molecule d = fm->find_by_name ("accordion-accDot");
630           d.translate_axis (staff_space * 0.5 PT, Y_AXIS);
631           m.add_molecule (d);
632           reg = reg.right_string (reg.length ()-1);
633         }
634     }
635   else if (sym == "Stdbase")
636     {
637       Molecule r = fm->find_by_name ("accordion-accStdbase");
638       m.add_molecule (r);
639       if (reg.left_string (1) == "T")
640         {
641           Molecule d = fm->find_by_name ("accordion-accDot");
642           d.translate_axis (staff_space * 3.5 PT, Y_AXIS);
643           m.add_molecule (d);
644           reg = reg.right_string (reg.length ()-1);
645         }
646       if (reg.left_string (1) == "F")
647         {
648           Molecule d = fm->find_by_name ("accordion-accDot");
649           d.translate_axis (staff_space * 2.5 PT, Y_AXIS);
650           m.add_molecule (d);
651           reg = reg.right_string (reg.length ()-1);
652         }
653       if (reg.left_string (1) == "M")
654         {
655           Molecule d = fm->find_by_name ("accordion-accDot");
656           d.translate_axis (staff_space * 2 PT, Y_AXIS);
657           d.translate_axis (staff_space PT, X_AXIS);
658           m.add_molecule (d);
659           reg = reg.right_string (reg.length ()-1);
660         }
661       if (reg.left_string (1) == "E")
662         {
663           Molecule d = fm->find_by_name ("accordion-accDot");
664           d.translate_axis (staff_space * 1.5 PT, Y_AXIS);
665           m.add_molecule (d);
666           reg = reg.right_string (reg.length ()-1);
667         }
668       if (reg.left_string (1) == "S")
669         {
670           Molecule d = fm->find_by_name ("accordion-accDot");
671           d.translate_axis (staff_space * 0.5 PT, Y_AXIS);
672           m.add_molecule (d);
673           reg = reg.right_string (reg.length ()-1);
674         }
675     }
676   /* ugh maybe try to use regular font for S.B. and B.B and only use one font
677      for the rectangle */
678   else if (sym == "SB")
679     {
680       Molecule r = fm->find_by_name ("accordion-accSB");
681       m.add_molecule (r);
682     }
683   else if (sym == "BB")
684     {
685       Molecule r = fm->find_by_name ("accordion-accBB");
686       m.add_molecule (r);
687     }
688   else if (sym == "OldEE")
689     {
690       Molecule r = fm->find_by_name ("accordion-accOldEE");
691       m.add_molecule (r);
692     }
693   else if (sym == "OldEES")
694     {
695       Molecule r = fm->find_by_name ("accordion-accOldEES");
696       m.add_molecule (r);
697     }
698   return m;  
699 }
700
701 Molecule
702 Lookup::repeat_slash (Real w, Real s, Real t)
703 {
704   SCM wid = gh_double2scm (w);
705   SCM sl = gh_double2scm (s);
706   SCM thick = gh_double2scm (t);
707   SCM slashnodot = scm_list_n (ly_symbol2scm ("repeat-slash"),
708                             wid, sl, thick, SCM_UNDEFINED);
709
710   Box b (Interval (0, w + sqrt (sqr(t/s) + sqr (t))),
711          Interval (0, w * s));
712
713   return Molecule (b, slashnodot); //  http://slashnodot.org
714 }
715
716 Molecule
717 Lookup::bracket (Axis a, Interval iv, Real thick, Real protude)
718 {
719   Box b;
720   Axis other = Axis((a+1)%2);
721   b[a] = iv;
722   b[other] = Interval(-1, 1) * thick * 0.5;
723   
724   Molecule m =  filledbox (b);
725
726   b[a] = Interval (iv[UP] - thick, iv[UP]);
727   Interval oi = Interval (-thick/2, thick/2 + fabs (protude)) ;
728   oi *=  sign (protude);
729   b[other] = oi;
730   m.add_molecule (filledbox (b));
731   b[a] = Interval (iv[DOWN], iv[DOWN]  +thick);
732   m.add_molecule (filledbox(b));
733
734   return m;
735 }
736
737 Molecule
738 Lookup::triangle (Interval iv, Real thick, Real protude)
739 {
740   Box b ;
741   b[X_AXIS] = iv;
742   b[Y_AXIS] = Interval (0 <? protude , 0 >? protude);
743
744   SCM s = scm_list_n (ly_symbol2scm ("symmetric-x-triangle"),
745                       gh_double2scm (thick),
746                       gh_double2scm (iv.length()), 
747                       gh_double2scm (protude), SCM_UNDEFINED);
748
749   return Molecule (b, s);
750 }
751
752
753 /*
754   TODO: use rounded boxes.
755  */
756 LY_DEFINE(ly_bracket ,"ly:bracket",
757           4, 0, 0,
758           (SCM a, SCM iv, SCM t, SCM p),
759           "Make a bracket in direction @var{a}. The extent of the bracket is " 
760           "given by @var{iv}. The wings protude by an amount of @var{p}, which "
761           "may be negative. The thickness is given by @var{t}.")
762 {
763   SCM_ASSERT_TYPE(ly_axis_p (a), a, SCM_ARG1, __FUNCTION__, "axis") ;
764   SCM_ASSERT_TYPE(ly_number_pair_p (iv), iv, SCM_ARG2, __FUNCTION__, "number pair") ;
765   SCM_ASSERT_TYPE(gh_number_p (t), a, SCM_ARG3, __FUNCTION__, "number") ;
766   SCM_ASSERT_TYPE(gh_number_p (p), a, SCM_ARG4, __FUNCTION__, "number") ;
767
768
769   return Lookup::bracket ((Axis)gh_scm2int (a), ly_scm2interval (iv),
770                           gh_scm2double (t),
771                           gh_scm2double (p)).smobbed_copy ();
772 }
773