]> git.donarmstrong.com Git - lilypond.git/commitdiff
* ly/engraver-init.ly: remove Ledger_line_engraver from Voice
authorHan-Wen Nienhuys <hanwen@xs4all.nl>
Sun, 28 May 2006 18:03:49 +0000 (18:03 +0000)
committerHan-Wen Nienhuys <hanwen@xs4all.nl>
Sun, 28 May 2006 18:03:49 +0000 (18:03 +0000)
context. This fixes double ledger lines in output.

* lily/system.cc (get_paper_system): remove 3 layer limit.

* buildscripts/output-distance.py (SystemLink.distance): new
file. Compare signatures.

* python/safeeval.py (SafeEval.visitUnarySub): new file.

* lily/grob-array.cc (remove_duplicates): rename from
uniq(). Sort before calling uniq() so it actually works.

ChangeLog
buildscripts/output-distance.py [new file with mode: 0644]
lily/grob-array.cc
lily/include/grob-array.hh
lily/system.cc
ly/engraver-init.ly
python/safeeval.py [new file with mode: 0644]
scm/stencil.scm

index 554b5e46a3735d4b4afc9d7d403055cec1230f31..144493cd3c58838d36ee202db4c37c707c6d1813 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2006-05-28  Han-Wen Nienhuys  <hanwen@lilypond.org>
+
+       * ly/engraver-init.ly: remove Ledger_line_engraver from Voice
+       context. This fixes double ledger lines in output. 
+
+       * lily/system.cc (get_paper_system): remove 3 layer limit.
+
+       * buildscripts/output-distance.py (SystemLink.distance): new
+       file. Compare signatures.
+
+       * python/safeeval.py (SafeEval.visitUnarySub): new file.
+
+       * lily/grob-array.cc (remove_duplicates): rename from
+       uniq(). Sort before calling uniq() so it actually works.
+
 2006-05-27  Han-Wen Nienhuys  <hanwen@lilypond.org>
 
        * scripts/lilypond-book.py (Lilypond_file_snippet.ly): strip
diff --git a/buildscripts/output-distance.py b/buildscripts/output-distance.py
new file mode 100644 (file)
index 0000000..c534d8c
--- /dev/null
@@ -0,0 +1,273 @@
+#!@TARGET_PYTHON@
+import sys
+
+sys.path.insert (0, 'python')
+import safeeval
+
+X_AXIS = 0
+Y_AXIS = 1
+INFTY = 1e6
+
+OUTPUT_EXPRESSION_PENALTY = 100
+ORPHAN_GROB_PENALTY = 1000
+
+def max_distance (x1, x2):
+    dist = 0.0
+
+    for (p,q) in zip (x1, x2):
+        dist = max (abs (p-q), dist)
+        
+    return dist
+
+
+empty_interval = (INFTY, -INFTY)
+empty_bbox = (empty_interval, empty_interval)
+
+def interval_length (i):
+    return max (i[1]-i[0], 0) 
+    
+def interval_union (i1, i2):
+    return (min (i1[0], i2[0]),
+            max (i1[1], i2[1]))
+
+def interval_intersect (i1, i2):
+    return (max (i1[0], i2[0]),
+            min (i1[1], i2[1]))
+
+def bbox_union (b1, b2):
+    return (interval_union (b1[X_AXIS], b2[X_AXIS]),
+            interval_union (b2[Y_AXIS], b2[Y_AXIS]))
+            
+def bbox_intersection (b1, b2):
+    return (interval_intersect (b1[X_AXIS], b2[X_AXIS]),
+            interval_intersect (b2[Y_AXIS], b2[Y_AXIS]))
+
+def bbox_area (b):
+    return interval_length (b[X_AXIS]) * interval_length (b[Y_AXIS])
+
+def bbox_diameter (b):
+    return max (interval_length (b[X_AXIS]),
+                interval_length (b[Y_AXIS]))
+                
+
+def difference_area (a, b):
+    return bbox_area (a) - bbox_area (bbox_intersection (a,b))
+
+class GrobSignature:
+    def __init__ (self, exp_list):
+        (self.name, self.origin, bbox_x,
+         bbox_y, self.output_expression) = tuple (exp_list)
+        
+        self.bbox = (bbox_x, bbox_y)
+        self.centroid = (bbox_x[0] + bbox_x[1], bbox_y[0] + bbox_y[1])
+
+    def __repr__ (self):
+        return '%s: (%.2f,%.2f), (%.2f,%.2f)\n' % (self.name,
+                                                 self.bbox[0][0],
+                                                 self.bbox[0][1],
+                                                 self.bbox[1][0],
+                                                 self.bbox[1][1])
+                                                 
+    def axis_centroid (self, axis):
+        return apply (sum, self.bbox[axis])  / 2 
+    
+    def centroid_distance (self, other, scale):
+        return max_distance (self.centroid, other.centroid) / scale 
+        
+    def bbox_distance (self, other):
+        divisor = bbox_area (self.bbox) + bbox_area (other.bbox)
+
+        if divisor:
+            return (difference_area (self.bbox, other.bbox) +
+                    difference_area (other.bbox, self.bbox)) / divisor
+        else:
+            return 0.0
+        
+    def expression_distance (self, other):
+        if self.output_expression == other.output_expression:
+            return 0.0
+        else:
+            return OUTPUT_EXPRESSION_PENALTY
+
+    def distance(self, other, max_distance):
+        return (self.expression_distance (other)
+                + self.centroid_distance (other, max_distance)
+                + self.bbox_distance (other))
+            
+class SystemSignature:
+    def __init__ (self, grob_sigs):
+        d = {}
+        for g in grob_sigs:
+            val = d.setdefault (g.name, [])
+            val += [g]
+
+        self.grob_dict = d
+        self.set_all_bbox (grob_sigs)
+
+    def set_all_bbox (self, grobs):
+        self.bbox = empty_bbox
+        for g in grobs:
+            self.bbox = bbox_union (g.bbox, self.bbox)
+
+    def closest (self, grob_name, centroid):
+        min_d = INFTY
+        min_g = None
+        try:
+            grobs = self.grob_dict[grob_name]
+
+            for g in grobs:
+                d = max_distance (g.centroid, centroid)
+                if d < min_d:
+                    min_d = d
+                    min_g = g
+
+
+            return min_g
+
+        except KeyError:
+            return None
+    def grobs (self):
+        return reduce (lambda x,y: x+y, self.grob_dict.values(), [])
+
+class SystemLink:
+    def __init__ (self, system1, system2):
+        self.system1 = system1
+        self.system2 = system2
+        
+        self.link_list_dict = {}
+        self.back_link_dict = {}
+
+        for g in system1.grobs ():
+            closest = system2.closest (g.name, g.centroid)
+            
+            self.link_list_dict.setdefault (closest, [])
+            self.link_list_dict[closest].append (g)
+            self.back_link_dict[g] = closest
+
+    def distance (self):
+        d = 0.0
+
+        scale = max (bbox_diameter (self.system1.bbox),
+                     bbox_diameter (self.system2.bbox))
+                                      
+        for (g1,g2) in self.back_link_dict.items ():
+            if g2 == None:
+                d += ORPHAN_GROB_PENALTY
+            else:
+                d += g1.distance (g2, scale)
+
+        for (g1,g2s) in self.link_list_dict.items ():
+            if len (g2s) != 1:
+                print g1, g2s 
+                d += ORPHAN_GROB_PENALTY
+
+        return d
+        
+def read_signature_file (name):
+    exp_str = ("[%s]" % open (name).read ())
+    entries = safeeval.safe_eval (exp_str)
+
+    grob_sigs = [GrobSignature (e) for e in entries]
+    sig = SystemSignature (grob_sigs)
+    return sig
+
+
+
+
+def compare_directories (dir1, dir2):
+
+    pass
+
+
+################################################################
+# TESTING
+
+def test ():
+    def system (x):
+        print 'invoking', x
+        stat = os.system (x)
+        assert stat == 0
+        
+    import os
+    dir = 'output-distance-test'
+
+    print 'test results in dir'
+    system ('rm -rf ' + dir)
+    os.mkdir (dir)
+    os.chdir (dir)
+    ly_template = r"""#(set! toplevel-score-handler print-score-with-defaults)
+#(set! toplevel-music-handler
+ (lambda (p m)
+ (if (not (eq? (ly:music-property m 'void) #t))
+    (print-score-with-defaults
+    p (scorify-music m p)))))
+
+#(ly:set-option 'point-and-click)
+
+
+
+%(papermod)s
+
+\relative c {
+  c^"%(userstring)s" %(extragrob)s
+  }
+"""
+
+
+    dicts = [{ 'papermod' : '',
+               'name' : '20',
+               'extragrob': '',
+               'userstring': 'test' },
+             { 'papermod' : '#(set-global-staff-size 19.5)',
+               'name' : '19',
+               'extragrob': '',
+               'userstring': 'test' },
+             { 'papermod' : '',
+               'name' : '20expr',
+               'extragrob': '',
+               'userstring': 'blabla' },
+             { 'papermod' : '',
+               'name' : '20grob',
+               'extragrob': 'c4',
+               'userstring': 'test' }]
+
+
+    for d in dicts:
+        open (d['name'] + '.ly','w').write (ly_template % d)
+        
+    names = [d['name'] for d in dicts]
+    
+    system ('lilypond -ddump-signatures -b eps ' + ' '.join (names))
+    
+    sigs = dict ((n, read_signature_file ('%s-0.signature' % n)) for n in names)
+
+    combinations = {}
+    for (n1, s1) in sigs.items():
+        for (n2, s2) in sigs.items():
+            combinations['%s-%s' % (n1, n2)] = SystemLink (s1,s2).distance ()
+            
+
+    results =   combinations.items ()
+    results.sort ()
+    for k,v in results:
+        print '%-20s' % k, v
+
+    assert combinations['20-20'] == 0.0
+    assert combinations['20-20expr'] > 50.0
+    assert combinations['20-19'] < 10.0
+
+
+def test_sigs (a,b):
+    sa = read_signature_file (a)
+    sb = read_signature_file (b)
+    link = SystemLink (sa, sb)
+    print link.distance()
+
+if __name__ == '__main__':
+    if sys.argv[1:]:
+        test_sigs (sys.argv[1],
+                    sys.argv[2])
+    else:
+        test ()
+
+
index f9982f15149339041b4b9faabc2e7b280bbc4afb..dff5102a83c776406473e5ba60ed4ae6e54e8ee7 100644 (file)
@@ -83,8 +83,11 @@ Grob_array::clear ()
 }
 
 void
-Grob_array::uniq ()
+Grob_array::remove_duplicates ()
 {
+  assert (!ordered_);
+  
+  vector_sort (grobs_, default_compare);
   ::uniq (grobs_);
 }
 
index f80c83632dd56d566f5082dc276db52a6f246290..ac4ba3e0c94933c95ac48c586271583a1176dc7a 100644 (file)
@@ -29,7 +29,7 @@ public:
   Grob *grob (vsize i) { return grobs_.at (i); }
   vsize size () const { return grobs_.size (); }
   bool empty () const;
-  void uniq ();
+  void remove_duplicates ();
   void clear ();
   void add (Grob *x) { grobs_.push_back (x); }
   void set_array (vector<Grob*> const &src);
index d1581b9e06eb8dab032e7b0f1e1f5f3998969da6..dda8fdf1321182e7c8d2dd56e934ac9eb44d6a46 100644 (file)
@@ -47,13 +47,14 @@ System::init_elements ()
 {
   SCM scm_arr = Grob_array::make_array ();
   all_elements_ = unsmob_grob_array (scm_arr);
+  all_elements_->set_ordered (false);
   set_object ("all-elements", scm_arr);
 }
 
 Grob *
-System::clone (int count) const
+System::clone (int index) const
 {
-  return new System (*this, count);
+  return new System (*this, index);
 }
 
 int
@@ -171,7 +172,7 @@ System::get_paper_systems ()
   for (vsize i = 0; i < broken_intos_.size (); i++)
     {
       System *child = dynamic_cast<System*> (broken_intos_[i]);
-      child->all_elements_->uniq ();
+      child->all_elements_->remove_duplicates ();
     }
 
   if (be_verbose_global)
@@ -320,46 +321,61 @@ System::post_processing ()
     }
 }
 
+struct Layer_entry
+{
+  Grob *grob_;
+  int layer_;
+};
+
+bool
+operator< (Layer_entry  const &a,
+          Layer_entry  const &b)
+{
+  return a.layer_ < b.layer_;
+}
+
+
 SCM
 System::get_paper_system ()
 {
-  static int const LAYER_COUNT = 3;
-
   SCM exprs = SCM_EOL;
   SCM *tail = &exprs;
 
-  /* Output stencils in three layers: 0, 1, 2.  Default layer: 1.
+  vector<Layer_entry> entries;
+  for (vsize j = 0; j < all_elements_->size (); j++)
+    {
+      Layer_entry e;
+      e.grob_ = all_elements_->grob (j);
+      e.layer_ = robust_scm2int (e.grob_->get_property ("layer"), 1);
+      
+      entries.push_back (e); 
+    }
 
-  FIXME: softcode this.
-  */
-  for (int i = 0; i < LAYER_COUNT; i++)
-    for (vsize j = 0; j < all_elements_->size (); j++)
-      {
-       Grob *g = all_elements_->grob (j);
-       if (robust_scm2int (g->get_property ("layer"), 1) != i)
-         continue;
-       
-       Stencil st = g->get_print_stencil ();
-
-       if (st.expr() == SCM_EOL)
-         continue;
-
-       Offset o;
-       for (int a = X_AXIS; a < NO_AXES; a++)
-         o[Axis (a)] = g->relative_coordinate (this, Axis (a));
-
-       Offset extra = robust_scm2offset (g->get_property ("extra-offset"),
-                                         Offset (0, 0))
-         * Staff_symbol_referencer::staff_space (g);
-
-       /* Must copy the stencil, for we cannot change the stencil
-          cached in G.  */
-
-       st.translate (o + extra);
-
-       *tail = scm_cons (st.expr (), SCM_EOL);
-       tail = SCM_CDRLOC (*tail);
-      }
+  vector_sort (entries, default_compare);
+  for (vsize j = 0; j < entries.size (); j++)
+    {
+      Grob *g = entries[j].grob_;
+      Stencil st = g->get_print_stencil ();
+
+      if (st.expr() == SCM_EOL)
+       continue;
+      
+      Offset o;
+      for (int a = X_AXIS; a < NO_AXES; a++)
+       o[Axis (a)] = g->relative_coordinate (this, Axis (a));
+
+      Offset extra = robust_scm2offset (g->get_property ("extra-offset"),
+                                       Offset (0, 0))
+       * Staff_symbol_referencer::staff_space (g);
+
+      /* Must copy the stencil, for we cannot change the stencil
+        cached in G.  */
+
+      st.translate (o + extra);
+
+      *tail = scm_cons (st.expr (), SCM_EOL);
+      tail = SCM_CDRLOC (*tail);
+    }
 
   if (Stencil *me = get_stencil ())
     exprs = scm_cons (me->expr (), exprs);
index 9137bd6a9ed9d457ba2c50c11d7e5843776d83f2..8ad404160ada8de89be997218d758ecc91023e9b 100644 (file)
@@ -141,7 +141,6 @@ contained staves are not connected vertically."
   \consists "Separating_line_group_engraver"   
   \consists "Dot_column_engraver"
   \consists "Bar_engraver"
-  \consists "Ledger_line_engraver" 
   \consists "Staff_symbol_engraver"
   \consists "Pitch_squash_engraver"
   \consists "Time_signature_engraver"
diff --git a/python/safeeval.py b/python/safeeval.py
new file mode 100644 (file)
index 0000000..4107620
--- /dev/null
@@ -0,0 +1,84 @@
+
+## http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/364469
+import compiler
+
+class Unsafe_Source_Error(Exception):
+    def __init__(self,error,descr = None,node = None):
+        self.error = error
+        self.descr = descr
+        self.node = node
+        self.lineno = getattr(node,"lineno",None)
+        
+    def __repr__(self):
+        return "Line %d.  %s: %s" % (self.lineno, self.error, self.descr)
+    __str__ = __repr__    
+           
+class SafeEval(object):
+    
+    def visit(self, node,**kw):
+        cls = node.__class__
+        meth = getattr(self,'visit'+cls.__name__,self.default)
+        return meth(node, **kw)
+            
+    def default(self, node, **kw):
+        for child in node.getChildNodes():
+            return self.visit(child, **kw)
+            
+    visitExpression = default
+    
+    def visitConst(self, node, **kw):
+        return node.value
+
+    def visitDict(self,node,**kw):
+        return dict([(self.visit(k),self.visit(v)) for k,v in node.items])
+        
+    def visitTuple(self,node, **kw):
+        return tuple(self.visit(i) for i in node.nodes)
+        
+    def visitList(self,node, **kw):
+        return [self.visit(i) for i in node.nodes]
+
+    def visitUnarySub(self, node, **kw):
+        return - self.visit (node.getChildNodes ()[0])
+
+class SafeEvalWithErrors(SafeEval):
+
+    def default(self, node, **kw):
+        raise Unsafe_Source_Error("Unsupported source construct",
+                                node.__class__,node)
+            
+    def visitName(self,node, **kw):
+        raise Unsafe_Source_Error("Strings must be quoted", 
+                                 node.name, node)
+                                 
+    # Add more specific errors if desired
+            
+
+def safe_eval(source, fail_on_error = True):
+    walker = fail_on_error and SafeEvalWithErrors() or SafeEval()
+    try:
+        ast = compiler.parse(source,"eval")
+    except SyntaxError, err:
+        raise
+    try:
+        return walker.visit(ast)
+    except Unsafe_Source_Error, err:
+        raise
+
+
+def safe_eval(source, fail_on_error = True):
+    walker = fail_on_error and SafeEvalWithErrors() or SafeEval()
+    try:
+        ast = compiler.parse(source,"eval")
+    except SyntaxError, err:
+        raise
+    try:
+        return walker.visit(ast)
+    except Unsafe_Source_Error, err:
+        raise
+
+def test ():
+    print safe_eval ('{1: [2,3], "4": (-1,2)}')
+    
+if __name__ == '__main__':
+    test ()
index 539b9c849b32bfad8461e881edfa6c5554e14962..fdbefd1f0ff04e9abe6f83a4b30e3d2fe13281f0 100644 (file)
@@ -352,7 +352,7 @@ grestore
         ((eq? head 'combine-stencil)
          (for-each (lambda (e) (interpret e))  (cdr expr)))
         (else
-         (collect (ffold-false-pairs (strip-floats expr))))
+         (collect (fold-false-pairs (strip-floats expr))))
         
         )))