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-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
--- /dev/null
+#!@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 ()
+
+
}
void
-Grob_array::uniq ()
+Grob_array::remove_duplicates ()
{
+ assert (!ordered_);
+
+ vector_sort (grobs_, default_compare);
::uniq (grobs_);
}
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);
{
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
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)
}
}
+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);
\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"
--- /dev/null
+
+## 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 ()
((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))))
)))