X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fstencil.cc;h=51f051d597f3588e954db618928d7f734173e77e;hb=0b544cfb7332615ef809b71b57ab656741311ae1;hp=c241fa18c271b4b08169780c85f63eb4d1ddfef6;hpb=0e5d83a9ceb4a143f83d22406d7eb816314ff9f7;p=lilypond.git diff --git a/lily/stencil.cc b/lily/stencil.cc index c241fa18c2..51f051d597 100644 --- a/lily/stencil.cc +++ b/lily/stencil.cc @@ -1,9 +1,20 @@ /* - stencil.cc -- implement Stencil + This file is part of LilyPond, the GNU music typesetter. - source file of the GNU LilyPond music typesetter + Copyright (C) 1997--2014 Han-Wen Nienhuys - (c) 1997--2009 Han-Wen Nienhuys + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see . */ #include "stencil.hh" @@ -57,8 +68,13 @@ bool Stencil::is_empty () const { return (expr_ == SCM_EOL - || dim_[X_AXIS].is_empty () - || dim_[Y_AXIS].is_empty ()); + || dim_.is_empty ()); +} + +bool +Stencil::is_empty (Axis a) const +{ + return dim_.is_empty (a); } SCM @@ -76,7 +92,7 @@ Stencil::extent_box () const void Stencil::rotate (Real a, Offset off) { - rotate_degrees (a * 180/M_PI, off); + rotate_degrees (a * 180 / M_PI, off); } /* @@ -101,9 +117,9 @@ Stencil::rotate_degrees_absolute (Real a, Offset absolute_off) */ expr_ = scm_list_n (ly_symbol2scm ("rotate-stencil"), - scm_list_2 (scm_from_double (a), - scm_cons (scm_from_double (x), scm_from_double (y))), - expr_, SCM_UNDEFINED); + scm_list_2 (scm_from_double (a), + scm_cons (scm_from_double (x), scm_from_double (y))), + expr_, SCM_UNDEFINED); /* * Calculate the new bounding box @@ -112,10 +128,10 @@ Stencil::rotate_degrees_absolute (Real a, Offset absolute_off) shifted_box.translate (-absolute_off); vector pts; - pts.push_back (Offset (shifted_box.x ().at(LEFT), shifted_box.y ().at(DOWN))); - pts.push_back (Offset (shifted_box.x ().at(RIGHT), shifted_box.y ().at(DOWN))); - pts.push_back (Offset (shifted_box.x ().at(RIGHT), shifted_box.y ().at(UP))); - pts.push_back (Offset (shifted_box.x ().at(LEFT), shifted_box.y ().at(UP))); + pts.push_back (Offset (shifted_box.x ().at (LEFT), shifted_box.y ().at (DOWN))); + pts.push_back (Offset (shifted_box.x ().at (RIGHT), shifted_box.y ().at (DOWN))); + pts.push_back (Offset (shifted_box.x ().at (RIGHT), shifted_box.y ().at (UP))); + pts.push_back (Offset (shifted_box.x ().at (LEFT), shifted_box.y ().at (UP))); const Offset rot = complex_exp (Offset (0, a * M_PI / 180.0)); dim_.set_empty (); @@ -147,24 +163,25 @@ Stencil::translate (Offset o) while (a < NO_AXES) { if (isinf (o[a]) - || isnan (o[a]) - - // ugh, hardcoded. - || fabs (o[a]) > 1e6) - { - programming_error (String_convert::form_string ("Improbable offset for stencil: %f staff space", o[a]) - + "\n" - + "Setting to zero."); - o[a] = 0.0; - if (strict_infinity_checking) - scm_misc_error (__FUNCTION__, "Improbable offset.", SCM_EOL); - } + || isnan (o[a]) + + // ugh, hardcoded. + || fabs (o[a]) > 1e6) + { + programming_error (String_convert::form_string ("Improbable offset for stencil: %f staff space", o[a]) + + "\n" + + "Setting to zero."); + o[a] = 0.0; + if (strict_infinity_checking) + scm_misc_error (__FUNCTION__, "Improbable offset.", SCM_EOL); + } incr (a); } - expr_ = scm_list_n (ly_symbol2scm ("translate-stencil"), - ly_offset2scm (o), - expr_, SCM_UNDEFINED); + if (!scm_is_null (expr_)) + expr_ = scm_list_n (ly_symbol2scm ("translate-stencil"), + ly_offset2scm (o), + expr_, SCM_UNDEFINED); if (!is_empty ()) dim_.translate (o); } @@ -177,10 +194,42 @@ Stencil::translate_axis (Real x, Axis a) translate (o); } +void +Stencil::scale (Real x, Real y) +{ + expr_ = scm_list_3 (ly_symbol2scm ("scale-stencil"), + scm_list_2 (scm_from_double (x), + scm_from_double (y)), + expr_); + dim_[X_AXIS] *= x; + dim_[Y_AXIS] *= y; +} + void Stencil::add_stencil (Stencil const &s) { - expr_ = scm_list_3 (ly_symbol2scm ("combine-stencil"), s.expr_, expr_); + SCM cs = ly_symbol2scm ("combine-stencil"); + if (scm_is_null (expr_)) + expr_ = s.expr_; + else if (scm_is_null (s.expr_)) + ; + else if (scm_is_pair (expr_) + && scm_is_eq (cs, scm_car (expr_))) + { + if (scm_is_pair (s.expr_) + && scm_is_eq (cs, scm_car (s.expr_))) + expr_ = scm_append (scm_list_2 (s.expr_, scm_cdr (expr_))); + else + expr_ = scm_cons2 (cs, s.expr_, scm_cdr (expr_)); + } + else + { + if (scm_is_pair (s.expr_) + && scm_is_eq (cs, scm_car (s.expr_))) + expr_ = scm_append (scm_list_2 (s.expr_, scm_list_1 (expr_))); + else + expr_ = scm_list_3 (cs, s.expr_, expr_); + } dim_.unite (s.dim_); } @@ -202,7 +251,7 @@ Stencil::set_empty (bool e) void Stencil::align_to (Axis a, Real x) { - if (is_empty ()) + if (is_empty (a)) return; Interval i (extent (a)); @@ -210,37 +259,164 @@ Stencil::align_to (Axis a, Real x) } /* See scheme Function. */ + +// Any stencil that is empty in the orthogonal axis is spacing. +// Spacing is not subjected to the max (0) rule and can thus be +// negative. + void Stencil::add_at_edge (Axis a, Direction d, Stencil const &s, Real padding) { - Interval my_extent = dim_[a]; - Interval i (s.extent (a)); - Real his_extent; - if (i.is_empty ()) + // Material that is empty in the axis of reference has only limited + // usefulness for combining. We still retain as much information as + // available since there may be uses like setting page links or + // background color or watermarks, and off-axis extents. + + if (is_empty (a)) { - programming_error ("Stencil::add_at_edge: adding empty stencil."); - his_extent = 0.0; + add_stencil (s); + return; } - else - his_extent = i[-d]; - Real offset = (my_extent.is_empty () ? 0.0 : my_extent[d] - his_extent) - + d * padding; + Interval first_extent = extent (a); + + if (s.is_empty (a)) + { + Stencil toadd (s); + // translation does not affect axis-empty extent box. + toadd.translate_axis (first_extent[d], a); + add_stencil (toadd); + return; + } + + Interval next_extent = s.extent (a); + + bool first_is_spacing = is_empty (other_axis (a)); + bool next_is_spacing = s.is_empty (other_axis (a)); + + Real offset = first_extent[d] - next_extent[-d]; + + if (!(first_is_spacing || next_is_spacing)) + { + offset += d * padding; + } Stencil toadd (s); toadd.translate_axis (offset, a); add_stencil (toadd); } +// Stencil::stack is mainly used for assembling lines or columns +// of stencils. For the most common case of adding at the right, the +// reference point of the added stencil is usually placed at the right +// edge of the current one, unless the added stencil has a negative +// left extent in which case its left edge is placed at the right edge +// of the current one. +// +// Spacing is special in that it is applied without padding. Spacing +// at the right edge shifts the right edge accordingly. +// +// For spacing at the left edge, there are several approaches. In +// order to get to predictable behavior, we want to have at least a +// continuous approach. An obvious idea is to do a "translate" by the +// appropriate amount. Doing that while retaining the nominal left +// edge seems like the most straightforward way. + +void +Stencil::stack (Axis a, Direction d, Stencil const &s, Real padding, Real mindist) +{ + // Material that is empty in the axis of reference can't be sensibly + // stacked. We just revert to add_at_edge behavior then. + + if (is_empty (a)) + { + Stencil toadd (s); + toadd.add_stencil (*this); + expr_ = toadd.expr (); + dim_ = toadd.extent_box (); + return; + } + + Interval first_extent = extent (a); + + if (s.is_empty (a)) + { + Stencil toadd (s); + toadd.translate_axis (first_extent[d], a); + toadd.add_stencil (*this); + expr_ = toadd.expr (); + dim_ = toadd.extent_box (); + return; + } + + Interval next_extent = s.extent (a); + + // It is somewhat tedious to special-case all spacing, but it turns + // out that not doing so makes it astonishingly hard to make the + // code do the correct thing. + + // If first is spacing, we translate second accordingly without + // letting this affect its backward edge. + if (is_empty (other_axis (a))) + { + Stencil toadd (s); + Real offset = d * first_extent.delta (); + toadd.translate_axis (offset, a); + toadd.add_stencil (*this); + expr_ = toadd.expr (); + dim_ = toadd.extent_box (); + dim_[a][-d] = next_extent[-d]; + dim_[a][d] = next_extent[d] + offset; + return; + } + + // If next is spacing, similar action: + if (s.is_empty (other_axis (a))) + { + Stencil toadd (s); + Real offset = first_extent [d]; + toadd.translate_axis (offset, a); + toadd.add_stencil (*this); + expr_ = toadd.expr (); + dim_ = toadd.extent_box (); + dim_[a][-d] = first_extent[-d]; + dim_[a][d] = first_extent[d] + d * next_extent.delta (); + return; + } + + + Real offset = first_extent[d]; + + // If the added stencil has a backwardly protruding edge, we make + // room for it when combining. + + if (d * next_extent [-d] < 0) + offset -= next_extent [-d]; + + offset += d * padding; + + if (offset * d < mindist) + offset = d * mindist; + + Stencil toadd (s); + toadd.translate_axis (offset, a); + toadd.add_stencil (*this); + expr_ = toadd.expr (); + dim_ = toadd.extent_box (); + dim_[a][-d] = first_extent [-d]; + dim_[a][d] = next_extent [d] + offset; +} + + Stencil Stencil::in_color (Real r, Real g, Real b) const { Stencil new_stencil (extent_box (), - scm_list_3 (ly_symbol2scm ("color"), - scm_list_3 (scm_from_double (r), - scm_from_double (g), - scm_from_double (b)), - expr ())); + scm_list_3 (ly_symbol2scm ("color"), + scm_list_3 (scm_from_double (r), + scm_from_double (g), + scm_from_double (b)), + expr ())); return new_stencil; }