]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/stencil.cc
* lily/modified-font-metric.cc (text_dimension): try
[lilypond.git] / lily / stencil.cc
index 2b16efe8b66b1f6a7a54dadc6fdae313912e2add..c684dcb31c7d7fc4feeb5cd12532f88592b290c3 100644 (file)
@@ -6,49 +6,80 @@
   (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
 */
 
+#include "stencil.hh"
+
 #include <math.h>
+
 #include <libc-extension.hh>   // isinf
 
-#include "font-metric.hh" 
+#include "input-smob.hh"
+#include "font-metric.hh"
 #include "dimensions.hh"
-#include "interval.hh"
-#include "string.hh"
-#include "stencil.hh"
+#include "string-convert.hh"
 #include "warn.hh"
 
-
 #include "ly-smobs.icc"
 
+Stencil::Stencil ()
+{
+  expr_ = SCM_EOL;
+  set_empty (true);
+}
 
-SCM
-Stencil::smobbed_copy () const
+Stencil::Stencil (Box b, SCM func)
 {
-  Stencil *s = new Stencil (*this);
-  return s->smobbed_self ();
+  expr_ = func;
+  dim_ = b;
 }
 
-Offset
-Stencil::origin () const
+int
+Stencil::print_smob (SCM, SCM port, scm_print_state *)
 {
-  return origin_;
+  scm_puts ("#<Stencil ", port);
+  scm_puts (" >", port);
+  return 1;
 }
 
+SCM
+Stencil::mark_smob (SCM smob)
+{
+  Stencil *s = (Stencil*) SCM_CELL_WORD_1 (smob);
+  return s->expr_;
+}
+
+IMPLEMENT_SIMPLE_SMOBS (Stencil);
+IMPLEMENT_TYPE_P (Stencil, "ly:stencil?");
+IMPLEMENT_DEFAULT_EQUAL_P (Stencil);
+
 Interval
 Stencil::extent (Axis a) const
 {
   return dim_[a];
 }
 
-Stencil::Stencil (Box b, SCM func)
+/* Hmm... maybe this is not such a good idea ; stuff can be empty,
+   while expr_ == '()  */
+bool
+Stencil::is_empty () const
 {
-  expr_ = func;
-  dim_ = b;
+  return expr_ == SCM_EOL;
 }
 
-Stencil::Stencil ()
+SCM
+Stencil::expr () const
 {
-  expr_ = SCM_EOL;
-  set_empty (true);
+  return expr_;
+}
+
+Box
+Stencil::extent_box () const
+{
+  return dim_;
+}
+Offset
+Stencil::origin () const
+{
+  return origin_;
 }
 
 void
@@ -57,23 +88,24 @@ Stencil::translate (Offset o)
   Axis a = X_AXIS;
   while (a < NO_AXES)
     {
-      if (abs (o[a]) > 100 CM
-         || isinf (o[a]) || isnan (o[a]))
+      if (isinf (o[a]) || isnan (o[a]))
        {
-         programming_error ("Improbable offset for translation: setting to zero");
+         programming_error (String_convert::form_string ("Improbable offset for stencil: %f staff space", o[a])
+                            + "\n"
+                            + "Setting to zero.");
          o[a] =  0.0;
        }
       incr (a);
     }
 
   expr_ = scm_list_n (ly_symbol2scm ("translate-stencil"),
-                  ly_offset2scm (o),
-                  expr_, SCM_UNDEFINED);
+                     ly_offset2scm (o),
+                     expr_, SCM_UNDEFINED);
   if (!is_empty ())
     dim_.translate (o);
   origin_ += o;
 }
-  
+
 void
 Stencil::translate_axis (Real x, Axis a)
 {
@@ -85,11 +117,12 @@ Stencil::translate_axis (Real x, Axis a)
 void
 Stencil::add_stencil (Stencil const &s)
 {
-  expr_ = scm_list_n (ly_symbol2scm ("combine-stencil"),
-                     s.expr_, expr_, SCM_UNDEFINED);
+  expr_ = scm_list_3 (ly_symbol2scm ("combine-stencil"), s.expr_, expr_);
   dim_.unite (s.dim_);
 }
 
+
+
 void
 Stencil::set_empty (bool e)
 {
@@ -109,87 +142,180 @@ void
 Stencil::align_to (Axis a, Real x)
 {
   if (is_empty ())
-    return ;
+    return;
 
   Interval i (extent (a));
   translate_axis (-i.linear_combination (x), a);
 }
 
-/*
-  TODO: unintuitive naming, you would expect *this to be moved.  Kept
-  API for compat with add_at_edge ().
-*/
+/* FIXME: unintuitive naming, you would expect *this to be moved.
+   Kept (keeping?) API for compat with add_at_edge ().
+
+   What is PADDING, what is MINIMUM, exactly?  */
 Stencil
-Stencil::moved_to_edge (Axis a, Direction d, Stencil const &s, Real padding,
-                       Real minimum) const
+Stencil::moved_to_edge (Axis a, Direction d, Stencil const &s,
+                       Real padding, Real minimum) const
 {
-  Real my_extent= is_empty () ? 0.0 : dim_[a][d];
+  Interval my_extent = dim_[a];
   Interval i (s.extent (a));
   Real his_extent;
   if (i.is_empty ())
     {
-      programming_error ("Stencil::move_to_edge: adding empty stencil.");
+      programming_error ("Stencil::moved_to_edge: adding empty stencil.");
       his_extent = 0.0;
+      SCM_ASSERT_TYPE(0, SCM_EOL, SCM_ARG1, __FUNCTION__, "");
     }
   else
     his_extent = i[-d];
 
-  Real offset = (my_extent -  his_extent) + d * padding;
+  Real offset = (my_extent.is_empty () ? 0.0 : my_extent[d] - his_extent)
+    + d * padding;
 
   Stencil toadd (s);
   toadd.translate_axis (offset,a);
 
-  if (minimum > 0
-      && d *(- origin ()[a] + toadd.origin ()[a]) < minimum)
+  if (minimum > 0 && d * (-origin ()[a] + toadd.origin ()[a]) < minimum)
     toadd.translate_axis ( -toadd.origin ()[a]
-                          + origin ()[a] + d* minimum, a);
-    
+                          + origin ()[a] + d * minimum, a);
+
   return toadd;
 }
 
 /*  See scheme Function.  */
 void
 Stencil::add_at_edge (Axis a, Direction d, Stencil const &s, Real padding,
-                      Real minimum)
+                     Real minimum)
 {
-  add_stencil (moved_to_edge (a,d,s,padding, minimum));
+  add_stencil (moved_to_edge (a, d, s, padding, minimum));
 }
 
-/* Hmm... maybe this is not such a good idea ; stuff can be empty,
-   while expr_ == '()  */
-bool
-Stencil::is_empty () const
+
+/****************************************************************/
+
+
+void
+interpret_stencil_expression (SCM expr,
+                             void (*func) (void*, SCM),
+                             void *func_arg,
+                             Offset o)
 {
-  return expr_ == SCM_EOL;
+  while (1)
+    {
+      if (!scm_is_pair (expr))
+        return;
+
+      SCM head = scm_car (expr);
+
+      if (head == ly_symbol2scm ("translate-stencil"))
+        {
+          o += ly_scm2offset (scm_cadr (expr));
+          expr = scm_caddr (expr);
+        }
+      else if (head == ly_symbol2scm ("combine-stencil"))
+        {
+          for (SCM x = scm_cdr (expr); scm_is_pair (x); x = scm_cdr (x))
+           interpret_stencil_expression (scm_car (x), func, func_arg, o);
+          return;
+        }
+      else if (head == ly_symbol2scm ("grob-cause"))
+       {
+         SCM grob = scm_cadr (expr);
+       
+         (*func) (func_arg, scm_list_2 (head, grob));
+         interpret_stencil_expression (scm_caddr (expr), func, func_arg, o);
+         (*func) (func_arg, scm_list_1 (ly_symbol2scm ("no-origin")));
+         return;
+       }
+      else
+        {
+          (*func) (func_arg,
+                   scm_list_4 (ly_symbol2scm ("placebox"),
+                               scm_make_real (o[X_AXIS]),
+                               scm_make_real (o[Y_AXIS]),
+                               expr));
+           return;
+        }
+    }
+}
+
+
+struct Font_list
+{
+  SCM fonts_;
+};
+
+static void
+find_font_function (void *fs, SCM x)
+{
+  Font_list *me = (Font_list*) fs;
+
+  if (scm_car (x) == ly_symbol2scm ("placebox"))
+    {
+      SCM args = scm_cdr (x);
+      SCM what = scm_caddr (args);
+
+      if (scm_is_pair (what))
+        {
+          SCM head = scm_car (what);
+          if (ly_symbol2scm ("text") == head)
+            me->fonts_ = scm_cons (scm_cadr (what), me->fonts_);
+          else if (head == ly_symbol2scm ("char"))
+            me->fonts_ = scm_cons (scm_cadr (what), me->fonts_);
+        }
+    }
 }
 
 SCM
-Stencil::get_expr () const
+find_expression_fonts (SCM expr)
 {
-  return expr_;
+  Font_list fl;
+
+  fl.fonts_ = SCM_EOL;
+
+  interpret_stencil_expression (expr, &find_font_function,
+                               (void*) &fl, Offset (0,0));
+
+  return fl.fonts_;
 }
 
-Box
-Stencil::extent_box () const
+
+LY_DEFINE (ly_stencil_fonts, "ly:stencil-fonts",
+          1, 0, 0, (SCM s),
+         " Analyse @var{s}, and return a list of fonts used in @var{s}.")
 {
-  return dim_;
+  Stencil *stil = unsmob_stencil (s);
+  SCM_ASSERT_TYPE (stil, s, SCM_ARG1, __FUNCTION__, "Stencil");
+  return find_expression_fonts (stil->expr ());
 }
 
-int
-Stencil::print_smob (SCM , SCM port, scm_print_state *)
+struct Stencil_interpret_arguments
 {
-  scm_puts ("#<Stencil ", port);
-  scm_puts (" >", port);
-  return 1;
+  SCM func;
+  SCM arg1;
+};
+
+void stencil_interpret_in_scm (void *p, SCM expr)
+{
+  Stencil_interpret_arguments *ap = (Stencil_interpret_arguments*) p;
+  scm_call_2 (ap->func, ap->arg1, expr);
 }
 
-SCM
-Stencil::mark_smob (SCM s)
+
+
+LY_DEFINE (ly_interpret_stencil_expression, "ly:interpret-stencil-expression",
+           4, 0, 0, (SCM expr, SCM func, SCM arg1, SCM offset),
+           "Parse EXPR, feed bits to FUNC with first arg ARG1")
 {
-  Stencil  *r = (Stencil *) ly_cdr (s);
-  return r->expr_;
+  SCM_ASSERT_TYPE (ly_c_procedure_p(func), func, SCM_ARG1, __FUNCTION__,
+                  "procedure");
+
+  Stencil_interpret_arguments a;
+  a.func = func;
+  a.arg1 = arg1;
+  Offset o = ly_scm2offset (offset);
+
+  interpret_stencil_expression (expr, stencil_interpret_in_scm, (void*) &a, o);
+
+  return SCM_UNSPECIFIED;
 }
 
-IMPLEMENT_SIMPLE_SMOBS (Stencil);
-IMPLEMENT_TYPE_P (Stencil, "ly:stencil?");
-IMPLEMENT_DEFAULT_EQUAL_P (Stencil);