\override SpacingSpanner #'spacing-increment = #1.0
\override Slur #'height-limit = #1.5
}
- \context {
- \PianoStaff
- \override VerticalAlignment #'forced-distance = #10
- }
}
\midi {
\context {
\Score
\override SpacingSpanner #'common-shortest-duration = #(ly:make-moment 1 8)
}
- \context {
- \PianoStaff
- \override VerticalAlignment #'forced-distance = #13.0
- }
}
\midi {
Skyline_pair *skys = Skyline_pair::unsmob (g->get_property ("skylines"));
if (skys)
skylines = *skys;
- else
- programming_error ("no skylines for alignment-child\n");
/* this is perhaps an abuse of minimum-?-extent: maybe we should create
another property? But it seems that the only (current) use of
if (b[X_AXIS][LEFT] - 2*horizon_padding < last_affected_position[dir])
continue;
- if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
- warning (_f ("outside-staff object %s has an empty extent", elements[i]->name ().c_str ()));
- else
+ if (!b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty ())
{
boxes.clear ();
boxes.push_back (b);
initialize ();
}
+static SCM
+min_permission (SCM perm1, SCM perm2)
+{
+ if (perm1 == ly_symbol2scm ("force"))
+ return perm2;
+ if (perm1 == ly_symbol2scm ("allow")
+ && perm2 != ly_symbol2scm ("force"))
+ return perm2;
+ return SCM_EOL;
+}
+
/* find the forces for all possible lines and cache ragged_ and ragged_right_ */
void
Constrained_breaking::initialize ()
line.break_permission_ = c->get_property ("line-break-permission");
line.page_permission_ = c->get_property ("page-break-permission");
line.turn_permission_ = c->get_property ("page-turn-permission");
+
+ /* turn permission should always be stricter than page permission
+ and page permission should always be stricter than line permission */
+ line.page_permission_ = min_permission (line.break_permission_,
+ line.page_permission_);
+ line.turn_permission_ = min_permission (line.page_permission_,
+ line.turn_permission_);
max_ext = max (max_ext, extent.length ());
line.extent_ = extent;
aliases_ = scm_cons (sym, aliases_);
}
+/* we don't (yet) instrument context properties */
+void
+Context::instrumented_set_property (SCM sym, SCM val, const char*, int, const char*)
+{
+ internal_set_property (sym, val);
+}
+
void
Context::internal_set_property (SCM sym, SCM val)
{
#ifndef NDEBUG
static SCM modification_callback = SCM_EOL;
+static SCM cache_callback = SCM_EOL;
LY_DEFINE (ly_set_grob_modification_callback, "ly:set-grob-modification-callback",
1, 0, 0, (SCM cb),
"Specify a procedure that will be called every time lilypond modifies "
"a grob property. The callback will receive as arguments "
- "the grob that is being modified, the name of the C++ file in which "
- "the modification was requested, the line number in the C++ file in "
- "which the modification was requested, the property to be changed and "
+ "the grob that is being modified, "
+ "the name of the C++ file in which the modification was requested, "
+ "the line number in the C++ file in which the modification was requested, "
+ "the name of the function in which the modification was requested, "
+ "the property to be changed and "
"the new value for the property.")
{
LY_ASSERT_TYPE (ly_is_procedure, cb, 1);
modification_callback = cb;
return SCM_UNSPECIFIED;
}
-#endif
-SCM
-Grob::get_property_alist_chain (SCM def) const
+LY_DEFINE (ly_set_property_cache_callback, "ly:set-property-cache-callback",
+ 1, 0, 0, (SCM cb),
+ "Specify a procedure that will be called whenever lilypond calculates "
+ "a callback function and caches the result. The callback will "
+ "receive as arguments "
+ "the grob whose property it is, "
+ "the name of the property, "
+ "the name of the callback that calculated the property and "
+ "the new (cached) value of the property.")
{
- return scm_list_n (mutable_property_alist_,
- immutable_property_alist_,
- def,
- SCM_UNDEFINED);
+ LY_ASSERT_TYPE (ly_is_procedure, cb, 1);
+
+ cache_callback = cb;
+ return SCM_UNSPECIFIED;
}
-
-extern void check_interfaces_for_property (Grob const *me, SCM sym);
-
-#if 0
-
-/*
- We can't change signatures depending on NDEBUG, since NDEBUG comes
- over the command line and may be different per .cc file. This
- should be done through the macro expansion of get_property ().
- */
void
-Grob::internal_set_property (SCM sym, SCM v, char const *file, int line, char const *fun)
+Grob::instrumented_set_property (SCM sym, SCM v,
+ char const *file,
+ int line,
+ char const *fun)
{
- SCM grob_p = ly_lily_module_constant ("ly:grob?");
- SCM grob_list_p = ly_lily_module_constant ("grob-list?");
- SCM type = scm_object_property (sym, ly_symbol2scm ("backend-type?"));
-
- if (type == grob_p
- || type == grob_list_p
- || (unsmob_grob (v) && ly_symbol2scm ("cause") != sym))
- {
- scm_display (scm_list_2 (sym, type), scm_current_output_port ());
- assert (0);
- }
-
- internal_set_value_on_alist (&mutable_property_alist_,
- sym, v);
-
-
if (ly_is_procedure (modification_callback))
scm_apply_0 (modification_callback,
scm_list_n (self_scm (),
scm_from_int (line),
scm_from_locale_string (fun),
sym, v, SCM_UNDEFINED));
+ internal_set_property (sym, v);
}
-#else
+#endif
+
+SCM
+Grob::get_property_alist_chain (SCM def) const
+{
+ return scm_list_n (mutable_property_alist_,
+ immutable_property_alist_,
+ def,
+ SCM_UNDEFINED);
+}
+
+extern void check_interfaces_for_property (Grob const *me, SCM sym);
+
void
Grob::internal_set_property (SCM sym, SCM v)
{
sym, v);
}
-#endif
void
Grob::internal_set_value_on_alist (SCM *alist, SCM sym, SCM v)
*alist = scm_assq_remove_x (*alist, marker);
}
else
- internal_set_value_on_alist (alist, sym, value);
+ {
+#ifndef NDEBUG
+ if (ly_is_procedure (cache_callback))
+ scm_apply_0 (cache_callback,
+ scm_list_n (self_scm (),
+ sym,
+ proc,
+ value,
+ SCM_UNDEFINED));
+#endif
+ internal_set_value_on_alist (alist, sym, value);
+ }
return value;
}
Context *where_defined (SCM name_sym, SCM *value) const;
void unset_property (SCM var_sym);
+ void instrumented_set_property (SCM, SCM, const char*, int, const char*);
void internal_set_property (SCM var_sym, SCM value);
Context *create_context (Context_def *, string, SCM);
SCM internal_get_object (SCM symbol) const;
void internal_set_object (SCM sym, SCM val);
void internal_del_property (SCM symbol);
+ void instrumented_set_property (SCM, SCM, char const*, int, char const*);
void internal_set_property (SCM sym, SCM val);
/* messages */
TODO: include modification callback support here, perhaps
through intermediate Grob::instrumented_set_property( .. __LINE__ ).
*/
-#define set_property(x, y) internal_set_property (ly_symbol2scm (x), y)
+#define set_property(x, y) instrumented_set_property (ly_symbol2scm (x), y, __FILE__, __LINE__, __FUNCTION__)
#else
#define set_property(x, y) internal_set_property (ly_symbol2scm (x), y)
#endif
SCM type () const { return type_; }
SCM get_property_alist (bool mutble) const;
SCM internal_get_property (SCM sym) const;
+ void instrumented_set_property (SCM, SCM, const char*, int, const char*);
void internal_set_property (SCM sym, SCM val);
};
DECLARE_UNSMOB(Prob,prob);
static Grob *
line_spanner_common_parent (Grob *me)
{
- Grob *common = find_fixed_alignment_parent (me);
+ /* FIXME: what is the right thing to do here, now that PianoStaves don't
+ have fixed spacing? */
+ Grob *common = 0; //find_fixed_alignment_parent (me);
if (!common)
{
common = Staff_symbol_referencer::get_staff_symbol (me);
/* Only reachable if GUILE exits. That is an error. */
return 1;
}
+
+SCM atexit_list = SCM_EOL;
+
+LY_DEFINE (ly_atexit, "ly:atexit",
+ 2, 0, 0, (SCM proc, SCM args),
+ "Just before exiting, call the procedure given. "
+"If this is called multiple times, the procedures are called "
+"in LIFO order.")
+{
+ atexit_list = scm_cons (scm_cons (proc, args), atexit_list);
+ scm_gc_protect_object (atexit_list);
+ return SCM_UNSPECIFIED;
+}
+
+LY_DEFINE (ly_do_atexit, "ly:do-atexit",
+ 0, 0, 0, (),
+ "Call the atexit procedures.")
+{
+ for (SCM s = atexit_list; scm_is_pair (s); s = scm_cdr (s))
+ scm_apply_0 (scm_caar (s), scm_cdar (s));
+ return SCM_UNSPECIFIED;
+}
for (vsize i = 0; i < orig.size (); i++)
{
- if (ret.size () && ret.back ().page_permission_ == SCM_EOL)
+ if (ret.size () && !scm_is_symbol (ret.back ().page_permission_))
{
Line_details const &old = ret.back ();
Line_details compressed = orig[i];
{
command_column_->set_property ("page-break-permission", SCM_EOL);
command_column_->set_property ("line-break-permission", SCM_EOL);
- if (break_events_.size ())
- warning (_f ("break event at moment %d/%d was overridden by some other event, are you using bar checks?",
- now_mom ().num (), now_mom ().den ()));
+ for (vsize i = 0; i < break_events_.size (); i++)
+ {
+ SCM perm = break_events_[i]->get_property ("permission");
+ if (perm == ly_symbol2scm ("force") || perm == ly_symbol2scm ("allow"))
+ warning (_f ("forced break was overridden by some other event, should you be using bar checks?"));
+ }
}
else if (Paper_column::is_breakable (command_column_))
{
return (s == SCM_BOOL_F) ? SCM_EOL : scm_cdr (s);
}
+/* We don't (yet) instrument probs */
+void
+Prob::instrumented_set_property (SCM sym, SCM val, const char*, int, const char*)
+{
+ internal_set_property (sym, val);
+}
+
void
Prob::internal_set_property (SCM sym, SCM val)
{
Spring_description::is_sane () const
{
return (inverse_hooke_ >= 0)
- && ideal_ > 0
+ && ideal_ >= 0
&& !isinf (ideal_) && !isnan (ideal_)
&& (inverse_hooke_ == 0.0 || fabs (inverse_hooke_) > 1e-8)
;
Spaceable_grob::add_spring (Grob *me, Grob *other,
Real distance, Real inverse_strength)
{
- if (distance <= 0.0 || inverse_strength < 0.0)
+ if (distance < 0.0 || inverse_strength < 0.0)
{
programming_error ("adding reverse spring, setting to unit");
distance = 1.0;
--- /dev/null
+\version "2.11.15"
+
+#(use-modules (scm graphviz))
+
+#(define last-grob-action '())
+
+#(define sym-blacklist '())
+#(define sym-whitelist '())
+
+#(define file-line-blacklist '())
+#(define file-line-whitelist '())
+
+#(define grob-blacklist '())
+#(define grob-whitelist '())
+
+#(define (blacklist-symbol sym)
+ (set! sym-blacklist (cons sym sym-blacklist)))
+
+#(define (whitelist-symbol sym)
+ (set! sym-whitelist (cons sym sym-whitelist)))
+
+#(define graph (make-graph "graph.dot"))
+
+% an event is relevant if
+% (it is on some whitelist or all whitelists are empty)
+% and
+% (it isn't on any blacklist)
+
+#(define (relevant? grob file line prop)
+ (let ((file-line `(,file . ,line)))
+ (and
+ (or
+ (= 0 (length file-line-whitelist) (length sym-whitelist))
+ (memq prop sym-whitelist)
+ (member file-line file-line-whitelist))
+ (and
+ (not (memq prop sym-blacklist))
+ (not (member file-line file-line-blacklist))))))
+
+#(define (grob-event-node grob label cluster)
+ (let ((node-id (add-node graph label cluster))
+ (prev (assv grob last-grob-action)))
+ (if (pair? prev)
+ (add-edge graph (cdr prev) node-id))
+ (set! last-grob-action (assv-set! last-grob-action grob node-id))))
+
+#(define (truncate-value val)
+ (let ((val-str (format "~a" val)))
+ (string-take val-str (min 50 (string-length val-str)))))
+
+#(define (grob-mod grob file line func prop val)
+ (let* ((val-str (truncate-value val))
+ (label (format "~a\\n~a:~a\\n~a <- ~a" grob file line prop val-str)))
+ (if (relevant? grob file line prop)
+ (grob-event-node grob label file))))
+
+#(define (grob-cache grob prop callback value)
+ (let* ((val-str (truncate-value value))
+ (label (format "caching ~a.~a\\n~a -> ~a" grob prop callback value)))
+ (if (relevant? grob #f #f prop)
+ (grob-event-node grob label #f))))
+
+#(define (grob-create grob file line func)
+ (let ((label (format "~a\\n~a:~a" grob file line)))
+ (grob-event-node grob label file)))
+
+#(ly:set-grob-modification-callback grob-mod)
+#(ly:set-property-cache-callback grob-cache)
+%#(ly:set-grob-creation-callback grob-create)
--- /dev/null
+;;;; graphviz.scm -- utilities for creating graphviz output
+;;;;
+;;;; source file of the GNU LilyPond music typesetter
+;;;;
+;;;; (c) 2007 Joe Neeman <joeneeman@gmail.com>
+
+
+(define-module (scm graphviz)
+ #:use-module (lily)
+ #:export (make-graph add-node add-edge add-cluster))
+
+(define (make-graph filename)
+ (let ((empty-graph (list->vector (list filename '() '() '()))))
+ (ly:atexit write-graph (list empty-graph))
+ empty-graph))
+
+(define (filename g) (vector-ref g 0))
+(define (nodes g) (vector-ref g 1))
+(define (edges g) (vector-ref g 2))
+(define (clusters g) (vector-ref g 3))
+
+(define (add-cluster graph node-id cluster-name)
+ (let* ((cs (clusters graph))
+ (cluster (assq cluster-name cs))
+ (already-in-cluster (if cluster
+ (cdr cluster)
+ '())))
+ (vector-set! graph 3 (assq-set! cs
+ cluster-name
+ (cons node-id already-in-cluster)))))
+
+(define (add-node graph label . cluster-name)
+ (let* ((ns (nodes graph))
+ (id (length ns)))
+ (vector-set! graph 1 (cons `(,id . ,label) ns))
+ (if (and (not (null? cluster-name))
+ (string? (car cluster-name)))
+ (add-cluster graph id (car cluster-name)))
+ id))
+
+(define (add-edge graph node1 node2)
+ (vector-set! graph 2 (cons `(,node1 . ,node2) (edges graph))))
+
+(define (write-graph graph)
+ (let ((out (open-file (filename graph) "w"))
+ (ns (nodes graph))
+ (es (edges graph))
+ (cs (clusters graph)))
+ (ly:message (format "writing graph ~s..." (filename graph)))
+ (display "digraph G {\nrankdir=\"LR\"\nnode [shape=rectangle]\n" out)
+ (map (lambda (n) (display (format "~a [label=\"~a\"]\n" (car n) (cdr n)) out))
+ ns)
+ (map (lambda (e) (display (format "~a -> ~a\n" (car e) (cdr e)) out))
+ es)
+ (map (lambda (c)
+ (display (format "subgraph cluster_~a {\nlabel= \"~a\"\ncolor=blue\n"
+ (string-filter (car c) char-alphabetic?)
+ (car c))
+ out)
+ (map (lambda (n) (display (format "~a\n" n) out)) (cdr c))
+ (display "}\n" out))
+ cs)
+ (display "}" out)))
(ly:error (_ "failed files: ~S") (string-join failed))
(exit 1))
(begin
+ (ly:do-atexit)
;; HACK: be sure to exit with single newline
(ly:message "")
(exit 0)))))