]> git.donarmstrong.com Git - lilypond.git/blob - lily/cluster.cc
*** empty log message ***
[lilypond.git] / lily / cluster.cc
1 /*
2   cluster.cc -- implement Cluster
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2002--2003 Juergen Reuter <reuter@ipd.uka.de>
7
8   Han-Wen Nienhuys <hanwen@cs.uu.nl>
9
10 */
11
12 #include <stdio.h>
13 #include "cluster.hh"
14 #include "grob.hh"
15 #include "spanner.hh"
16 #include "item.hh"
17 #include "pitch.hh"
18 #include "staff-symbol-referencer.hh"
19 #include "lookup.hh"
20 #include "box.hh"
21 #include "interval.hh"
22 #include "paper-def.hh"
23 #include "paper-column.hh"
24 #include "note-column.hh"
25
26 /*
27  * TODO: Add support for cubic spline segments.
28  */
29 Molecule
30 brew_cluster_piece (Grob *me, Array<Offset> bottom_points, Array<Offset> top_points)
31 {
32 #if 0
33   Real blotdiameter = me->get_paper ()->get_var ("blotdiameter");
34 #else
35   Real blotdiameter = Staff_symbol_referencer::staff_space (me)/2;
36 #endif
37
38   Real padding;
39   SCM padding_scm = me->get_grob_property ("padding");
40   if (gh_number_p (padding_scm))
41     padding = gh_scm2double (padding_scm);
42   else
43     padding = 0.0;
44   Offset vpadding = Offset (0, padding);
45   Offset hpadding = Offset (0.5 * blotdiameter, 0);
46   Offset hvpadding = 0.5 * hpadding + vpadding;
47
48   SCM shape_scm = me->get_grob_property ("style");
49   String shape;
50   if (gh_symbol_p (shape_scm))
51     {
52       shape = ly_symbol2string (shape_scm);
53     }
54   else
55     {
56       shape = "leftsided-stairs";
57     }
58
59
60   Molecule out = Molecule ();
61   Array<Offset> points;
62   points.clear ();
63   int size = bottom_points.size ();
64   if (String::compare (shape, "leftsided-stairs") == 0)
65     {
66       for (int i = 0; i < size - 1; i++)
67         {
68           Box box;
69           box.add_point (bottom_points[i] - hvpadding);
70           box.add_point (Offset(top_points[i + 1][X_AXIS],
71                                 top_points[i][Y_AXIS]) + hvpadding);
72           out.add_molecule (Lookup::roundfilledbox (box, blotdiameter));
73         }
74     }
75   else if (String::compare (shape, "rightsided-stairs") == 0)
76     {
77       for (int i = 0; i < size - 1; i++)
78         {
79           Box box;
80           box.add_point (Offset(bottom_points[i][X_AXIS],
81                                 bottom_points[i + 1][Y_AXIS]) - hvpadding);
82           box.add_point (top_points[i + 1] + hvpadding);
83           out.add_molecule (Lookup::roundfilledbox (box, blotdiameter));
84         }
85     }
86   else if (String::compare (shape, "centered-stairs") == 0)
87     {
88       Real left_xmid = bottom_points[0][X_AXIS];
89       for (int i = 0; i < size - 1; i++)
90         {
91           Real right_xmid =
92             0.5 * (bottom_points[i][X_AXIS] + bottom_points[i + 1][X_AXIS]);
93           Box box;
94           box.add_point (Offset (left_xmid, bottom_points[i][Y_AXIS]) -
95                          hvpadding);
96           box.add_point (Offset (right_xmid, top_points[i][Y_AXIS]) +
97                          hvpadding);
98           out.add_molecule (Lookup::roundfilledbox (box, blotdiameter));
99           left_xmid = right_xmid;
100         }
101       Real right_xmid = bottom_points[size - 1][X_AXIS];
102       Box box;
103       box.add_point (Offset (left_xmid, bottom_points[size - 1][Y_AXIS]) -
104                      hvpadding);
105       box.add_point (Offset (right_xmid, top_points[size - 1][Y_AXIS]) +
106                      hvpadding);
107       out.add_molecule (Lookup::roundfilledbox (box, blotdiameter));
108     }
109   else if (String::compare (shape, "ramp") == 0)
110     {
111       points.push (bottom_points[0] - vpadding + hpadding);
112       for (int i = 1; i < size - 1; i++)
113         {
114           points.push (bottom_points[i] - vpadding);
115         }
116       points.push (bottom_points[size - 1] - vpadding - hpadding);
117       points.push (top_points[size - 1] + vpadding - hpadding);
118       for (int i = size - 2; i > 0; i--)
119         {
120           points.push (top_points[i] + vpadding);
121         }
122       points.push (top_points[0] + vpadding + hpadding);
123       out.add_molecule (Lookup::round_filled_polygon (points, blotdiameter));
124     }
125   else
126     {
127       me->warning (_f ("unknown cluster style `%s'", shape.to_str0 ()));
128     }
129   return out;
130 }
131
132 MAKE_SCHEME_CALLBACK (Cluster,brew_molecule,1);
133 SCM
134 Cluster::brew_molecule (SCM smob)
135 {
136   Grob *me = unsmob_grob (smob);
137
138   Spanner *spanner = dynamic_cast<Spanner*> (me);
139   if (!spanner)
140     {
141       me->programming_error ("Cluster::brew_molecule(): not a spanner");
142       return SCM_EOL;
143     }
144
145   Item *left_bound = spanner->get_bound (LEFT);
146   Item *right_bound = spanner->get_bound (RIGHT);
147
148   Grob *common = left_bound->common_refpoint (right_bound, X_AXIS);
149   SCM cols  =me->get_grob_property ("columns");
150
151   if (!gh_pair_p (cols))
152     {
153       me->warning ("junking empty cluster");
154       me->suicide ();
155       
156       return SCM_EOL;
157     }
158   common = common_refpoint_of_list (cols, common, X_AXIS);
159   Array<Offset> bottom_points;
160   Array<Offset> top_points;
161
162
163   Real left_coord = left_bound->relative_coordinate (common, X_AXIS);
164
165   for (SCM s = cols; gh_pair_p (s); s = ly_cdr (s))
166     {
167       Grob * col = unsmob_grob (ly_car (s));
168
169       SCM posns = col->get_grob_property ("positions");
170       
171       Slice s (0,0);
172       if (ly_number_pair_p (posns))
173         s = Slice (gh_scm2int (gh_car (posns)),
174                    gh_scm2int (gh_cdr (posns)));
175
176       Real x = col->relative_coordinate (common, X_AXIS) - left_coord;
177       bottom_points.push (Offset (x, s[DOWN] *0.5));
178       top_points.push (Offset (x, s[UP] * 0.5));
179     }
180
181   /*
182     Across a line break we anticipate on the next pitches.
183    */
184   if (spanner->original_)
185     {
186       Spanner *orig = dynamic_cast<Spanner*> (spanner->original_);
187       
188       if (spanner->break_index_ < orig->broken_intos_.size()-1)
189         {
190           Spanner * next = orig->broken_intos_[spanner->break_index_+1];
191           SCM cols = next->get_grob_property ("columns");
192           if (gh_pair_p (cols))
193             {
194               Grob * col = unsmob_grob (ly_car (scm_last_pair (cols)));
195               SCM posns = col->get_grob_property ("positions");
196       
197               Slice s (0,0);
198               if (ly_number_pair_p (posns))
199                 s = Slice (gh_scm2int (gh_car (posns)),
200                            gh_scm2int (gh_cdr (posns)));
201
202               Real x = right_bound->relative_coordinate (common,X_AXIS) - left_coord;
203               
204               bottom_points.insert (Offset (x, s[DOWN] * 0.5),0);
205               top_points.insert (Offset (x, s[UP] * 0.5),0);
206             }
207         }
208     }
209
210   bottom_points.reverse ();
211   top_points.reverse ();
212
213   Molecule out = brew_cluster_piece (me, bottom_points, top_points);
214   return out.smobbed_copy ();
215 }
216
217 ADD_INTERFACE (Cluster,"cluster-interface",
218   "A graphically drawn musical cluster. " 
219 "\n\n"
220 "@code{padding} adds to the vertical extent of the shape (top and "
221 "bottom) and is expressed in units of staffspace.  Since the pitch "
222 "range of a single pitch is infinitely small, if padding is set to "
223 "@code{0.0}, this possibly results in an invisible shape, if you,for "
224 "example, say @code{c-\\startCluster d e-\\endCluster}.  The default "
225 "value for @code{padding} therefore is @code{0.25}, such that a single "
226 "pitch roughly shows the same height as a note head. "
227 "\n\n"
228 "@code{style} controls the shape of cluster segments.  Valid values include 'leftsided-stairs', 'rightsided-stairs', 'centered-stairs', and 'ramp'.\n"
229 ,
230   "style padding columns");