]> git.donarmstrong.com Git - lilypond.git/blob - lily/separation-item.cc
Improvements in vertical skyline approximations (issue 2148).
[lilypond.git] / lily / separation-item.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1998--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "separation-item.hh"
21
22 #include "accidental-placement.hh"
23 #include "axis-group-interface.hh"
24 #include "lookup.hh"
25 #include "note-column.hh"
26 #include "note-head.hh"
27 #include "paper-column.hh"
28 #include "pointer-group-interface.hh"
29 #include "skyline-pair.hh"
30 #include "stencil.hh"
31 #include "warn.hh"
32
33 void
34 Separation_item::add_item (Grob *s, Item *i)
35 {
36   assert (i);
37   Pointer_group_interface::add_grob (s, ly_symbol2scm ("elements"), i);
38 }
39
40 void
41 Separation_item::add_conditional_item (Grob *me, Grob *e)
42 {
43   Pointer_group_interface::add_grob (me, ly_symbol2scm ("conditional-elements"), e);
44 }
45
46 Real
47 Separation_item::set_distance (Item *l, Item *r, Real padding)
48 {
49   Drul_array<Skyline_pair *> lines (Skyline_pair::unsmob (l->get_property ("horizontal-skylines")),
50                                     Skyline_pair::unsmob (r->get_property ("horizontal-skylines")));
51   Skyline right = conditional_skyline (r, l);
52   right.merge ((*lines[RIGHT])[LEFT]);
53
54   Real dist = padding + (*lines[LEFT])[RIGHT].distance (right);
55   if (dist > 0)
56     {
57       Rod rod;
58
59       rod.item_drul_ = Drul_array<Item *> (l, r);
60
61       rod.distance_ = dist;
62       rod.add_to_cols ();
63     }
64
65   return max (dist, 0.0);
66 }
67
68 bool
69 Separation_item::is_empty (Grob *me)
70 {
71   Skyline_pair *sky = Skyline_pair::unsmob (me->get_property ("horizontal-skylines"));
72   return (!sky || sky->is_empty ());
73 }
74
75 /*
76   Return the width of ME given that we are considering the object on
77   the LEFT.
78 */
79 Skyline
80 Separation_item::conditional_skyline (Grob *me, Grob *left)
81 {
82   vector<Box> bs = boxes (me, left);
83   return Skyline (bs, Y_AXIS, LEFT);
84 }
85
86 MAKE_SCHEME_CALLBACK (Separation_item, calc_skylines, 1);
87 SCM
88 Separation_item::calc_skylines (SCM smob)
89 {
90   Item *me = unsmob_item (smob);
91   vector<Box> bs = boxes (me, 0);
92   Skyline_pair sp (bs, Y_AXIS);
93   /*
94     TODO: We need to decide if padding is 'intrinsic'
95     to a skyline or if it is something that is only added on in
96     distance calculations.  Here, we make it intrinsic, which copies
97     the behavior from the old code but no longer corresponds to how
98     vertical skylines are handled (where padding is not built into
99     the skyline).
100   */
101   Real vp = robust_scm2double (me->get_property ("skyline-vertical-padding"), 0.0);
102   sp[LEFT] = sp[LEFT].padded (vp);
103   sp[RIGHT] = sp[RIGHT].padded (vp);
104   return sp.smobbed_copy ();
105 }
106
107 /* if left is non-NULL, get the boxes corresponding to the
108    conditional-elements (conditioned on the grob LEFT). This
109    sounds more general than it is: conditional-elements are
110    always accidentals attached to a tied note.
111 */
112 vector<Box>
113 Separation_item::boxes (Grob *me, Grob *left)
114 {
115   Item *item = dynamic_cast<Item *> (me);
116
117   int very_large = INT_MAX;
118   Paper_column *pc = item->get_column ();
119   vector<Box> out;
120   extract_grob_set (me, left ? "conditional-elements" : "elements", read_only_elts);
121   vector<Grob *> elts;
122
123   if (left)
124     elts = Accidental_placement::get_relevant_accidentals (read_only_elts, left);
125   else
126     elts = read_only_elts;
127
128   Grob *ycommon = common_refpoint_of_array (elts, me, Y_AXIS);
129
130   for (vsize i = 0; i < elts.size (); i++)
131     {
132       Item *il = dynamic_cast<Item *> (elts[i]);
133       if (pc != il->get_column ())
134         continue;
135
136       /* ugh. We want to exclude groups of grobs (so that we insert each grob
137          individually into the skyline instead of adding a single box that
138          bounds all of them). However, we can't exclude an axis-group that
139          adds to its childrens' stencil. Currently, this is just TrillPitchGroup;
140          hence the check for note-head-interface. */
141       if (Axis_group_interface::has_interface (il)
142           && !Note_head::has_interface (il))
143         continue;
144
145       Interval y (il->pure_height (ycommon, 0, very_large));
146       Interval x (il->extent (pc, X_AXIS));
147
148       Interval extra_width = robust_scm2interval (elts[i]->get_property ("extra-spacing-width"),
149                                                   Interval (-0.1, 0.1));
150       Interval extra_height = robust_scm2interval (elts[i]->get_property ("extra-spacing-height"),
151                                                    Interval (0.0, 0.0));
152
153       x[LEFT] += extra_width[LEFT];
154       x[RIGHT] += extra_width[RIGHT];
155       y[DOWN] += extra_height[DOWN];
156       y[UP] += extra_height[UP];
157
158       if (!x.is_empty () && !y.is_empty ())
159         out.push_back (Box (x, y));
160     }
161
162   return out;
163 }
164
165 MAKE_SCHEME_CALLBACK (Separation_item, print, 1)
166 SCM
167 Separation_item::print (SCM smob)
168 {
169   if (!debug_skylines)
170     return SCM_BOOL_F;
171
172   Grob *me = unsmob_grob (smob);
173   Stencil ret;
174   if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("horizontal-skylines")))
175     {
176       ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[LEFT].to_points (Y_AXIS)).in_color (255, 255, 0));
177       ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[RIGHT].to_points (Y_AXIS)).in_color (0, 255, 255));
178     }
179   return ret.smobbed_copy ();
180 }
181
182 ADD_INTERFACE (Separation_item,
183                "Item that computes widths to generate spacing rods.",
184
185                /* properties */
186                "X-extent "
187                "conditional-elements "
188                "elements "
189                "padding "
190                "horizontal-skylines "
191                "skyline-vertical-padding "
192               );