]> 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
14 #include "cluster.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 "warn.hh"
24
25
26 /*
27    TODO: Add support for cubic spline segments.
28
29  */
30 Molecule
31 brew_cluster_piece (Grob *me, Array<Offset> bottom_points, Array<Offset> top_points)
32 {
33   Real blotdiameter = Staff_symbol_referencer::staff_space (me)/2;
34
35   Real padding;
36   SCM padding_scm = me->get_grob_property ("padding");
37   if (gh_number_p (padding_scm))
38     padding = gh_scm2double (padding_scm);
39   else
40     padding = 0.0;
41   Offset vpadding = Offset (0, padding);
42   Offset hpadding = Offset (0.5 * blotdiameter, 0);
43   Offset hvpadding = 0.5 * hpadding + vpadding;
44
45   SCM shape_scm = me->get_grob_property ("style");
46   String shape;
47
48   if (gh_symbol_p (shape_scm))
49     {
50       shape = ly_symbol2string (shape_scm);
51     }
52   else
53     {
54       programming_error ("#'style should be symbol.");
55       me->suicide();
56       return  Molecule();
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::round_filled_box (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::round_filled_box (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::round_filled_box (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::round_filled_box (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   Real unit = Staff_symbol_referencer::staff_space (me) *0.5;
166
167   /*
168     TODO: should we move the cluster a little to the right to be in
169     line with the center of the note heads?
170     
171    */
172   for (SCM s = cols; gh_pair_p (s); s = ly_cdr (s))
173     {
174       Grob * col = unsmob_grob (ly_car (s));
175
176       SCM posns = col->get_grob_property ("positions");
177       
178       Slice s (0,0);
179       if (is_number_pair (posns))
180         s = Slice (gh_scm2int (gh_car (posns)),
181                    gh_scm2int (gh_cdr (posns)));
182
183       Real x = col->relative_coordinate (common, X_AXIS) - left_coord;
184       bottom_points.push (Offset (x, s[DOWN] *unit));
185       top_points.push (Offset (x, s[UP] * unit));
186     }
187
188   /*
189     Across a line break we anticipate on the next pitches.
190    */
191   if (spanner->original_)
192     {
193       Spanner *orig = dynamic_cast<Spanner*> (spanner->original_);
194       
195       if (spanner->break_index_ < orig->broken_intos_.size()-1)
196         {
197           Spanner * next = orig->broken_intos_[spanner->break_index_+1];
198           SCM cols = next->get_grob_property ("columns");
199           if (gh_pair_p (cols))
200             {
201               Grob * col = unsmob_grob (ly_car (scm_last_pair (cols)));
202               SCM posns = col->get_grob_property ("positions");
203       
204               Slice s (0,0);
205               if (is_number_pair (posns))
206                 s = Slice (gh_scm2int (gh_car (posns)),
207                            gh_scm2int (gh_cdr (posns)));
208
209               Real x = right_bound->relative_coordinate (common,X_AXIS) - left_coord;
210               
211               bottom_points.insert (Offset (x, s[DOWN] * unit),0);
212               top_points.insert (Offset (x, s[UP] * unit),0);
213             }
214         }
215     }
216
217   bottom_points.reverse ();
218   top_points.reverse ();
219
220   Molecule out = brew_cluster_piece (me, bottom_points, top_points);
221   return out.smobbed_copy ();
222 }
223
224 ADD_INTERFACE (Cluster,"cluster-interface",
225   "A graphically drawn musical cluster. " 
226 "\n\n"
227 "@code{padding} adds to the vertical extent of the shape (top and "
228 "bottom) and is expressed in units of staffspace.  Since the pitch "
229 "range of a single pitch is infinitely small, if padding is set to "
230 "@code{0.0}, this possibly results in an invisible shape, if you,for "
231 "example, say @code{c-\\startCluster d e-\\endCluster}.  The default "
232 "value for @code{padding} therefore is @code{0.25}, such that a single "
233 "pitch roughly shows the same height as a note head. "
234 "\n\n"
235 "@code{style} controls the shape of cluster segments.  Valid values include 'leftsided-stairs', 'rightsided-stairs', 'centered-stairs', and 'ramp'.\n"
236 ,
237   "style padding columns");