X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fgrob.cc;h=548c82f2c84d74f7ab0949c5adc15f62839d0274;hb=30a7c603fa9977c1a4a68868475d465e8f7132ca;hp=0406636264729f2c91de8c7e5f0eef72b8ce125d;hpb=c76d27c59244a331a2873a40e0d7e40ad378d4b8;p=lilypond.git diff --git a/lily/grob.cc b/lily/grob.cc index 0406636264..548c82f2c8 100644 --- a/lily/grob.cc +++ b/lily/grob.cc @@ -3,11 +3,12 @@ source file of the GNU LilyPond music typesetter - (c) 1997--2004 Han-Wen Nienhuys + (c) 1997--2005 Han-Wen Nienhuys */ +#include "grob.hh" -#include +#include #include #include "main.hh" @@ -17,34 +18,41 @@ #include "misc.hh" #include "paper-score.hh" #include "stencil.hh" -#include "grob.hh" #include "warn.hh" -#include "spanner.hh" #include "system.hh" #include "item.hh" #include "stencil.hh" #include "misc.hh" #include "music.hh" #include "item.hh" - +#include "paper-score.hh" #include "ly-smobs.icc" +#include "output-def.hh" + +Grob * +Grob::clone (int count) const +{ + return new Grob (*this, count); +} /* TODO: - - remove dynamic_cast and put this code into respective - subclass. */ +- remove dynamic_cast and put this code into respective +subclass. */ #define HASH_SIZE 3 #define INFINITY_MSG "Infinity or NaN encountered" -Grob::Grob (SCM basicprops) +Grob::Grob (SCM basicprops, + Object_key const *key) { + key_ = key; /* FIXME: default should be no callback. */ self_scm_ = SCM_EOL; - pscore_= 0; + pscore_ = 0; status_ = 0; original_ = 0; - immutable_property_alist_ = basicprops; + immutable_property_alist_ = basicprops; mutable_property_alist_ = SCM_EOL; /* We do smobify_self () as the first step. Since the object lives @@ -52,34 +60,38 @@ Grob::Grob (SCM basicprops) GC. After smobify_self (), they are. */ smobify_self (); + /* + We always get a new key object for a new grob. + */ + scm_gc_unprotect_object (key_->self_scm ()); SCM meta = get_property ("meta"); - if (ly_c_pair_p (meta)) + if (scm_is_pair (meta)) { SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta); /* Switch off interface checks for the moment. */ - bool itc = internal_type_checking_global_b; - internal_type_checking_global_b = false; - internal_set_property (ly_symbol2scm ("interfaces"), ly_cdr (ifs)); - internal_type_checking_global_b = itc; + bool itc = do_internal_type_checking_global; + do_internal_type_checking_global = false; + internal_set_property (ly_symbol2scm ("interfaces"), scm_cdr (ifs)); + do_internal_type_checking_global = itc; } /* TODO: - - destill this into a function, so we can re-init the immutable - properties with a new BASICPROPS value after - creation. Convenient eg. when using \override with - StaffSymbol. */ + - destill this into a function, so we can re-init the immutable + properties with a new BASICPROPS value after + creation. Convenient eg. when using \override with + StaffSymbol. */ - char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"}; - char const*xnames[] = {"X-extent", "Y-extent"}; - char const*enames[] = {"X-extent-callback", "Y-extent-callback"}; + char const *onames[] = {"X-offset-callbacks", "Y-offset-callbacks"}; + char const *xnames[] = {"X-extent", "Y-extent"}; + char const *enames[] = {"X-extent-callback", "Y-extent-callback"}; for (int a = X_AXIS; a <= Y_AXIS; a++) { SCM l = get_property (onames[a]); - if (scm_ilength (l) >=0) + if (scm_ilength (l) >= 0) { dim_cache_[a].offset_callbacks_ = l; dim_cache_[a].offsets_left_ = scm_ilength (l); @@ -88,24 +100,31 @@ Grob::Grob (SCM basicprops) programming_error ("[XY]-offset-callbacks must be a list"); SCM cb = get_property (enames[a]); - SCM xt = get_property (xnames[a]); + if (cb == SCM_BOOL_F) + { + dim_cache_[a].dimension_ = SCM_BOOL_F; + } - /* Should change default to empty? */ + SCM xt = get_property (xnames[a]); if (is_number_pair (xt)) - cb = xt; - else if (cb != SCM_BOOL_F - && !ly_c_procedure_p (cb) && !ly_c_pair_p (cb) - && ly_c_procedure_p (get_property ("print-function"))) - cb = stencil_extent_proc; - - dim_cache_[a].dimension_ = cb; + { + dim_cache_[a].dimension_ = xt; + } + else if (ly_c_procedure_p (cb)) + { + dim_cache_[a].dimension_callback_ = cb; + } + else if (cb == SCM_EOL + && ly_c_procedure_p (get_property ("print-function"))) + dim_cache_[a].dimension_callback_ = stencil_extent_proc; } } -Grob::Grob (Grob const &s) - : dim_cache_ (s.dim_cache_) +Grob::Grob (Grob const &s, int copy_index) + : dim_cache_ (s.dim_cache_) { - original_ = (Grob*) &s; + key_ = new Copied_key (s.key_, copy_index); + original_ = (Grob *) & s; self_scm_ = SCM_EOL; immutable_property_alist_ = s.immutable_property_alist_; @@ -117,6 +136,7 @@ Grob::Grob (Grob const &s) pscore_ = 0; smobify_self (); + scm_gc_unprotect_object (key_->self_scm ()); } Grob::~Grob () @@ -137,26 +157,24 @@ Grob::stencil_extent (SCM element_smob, SCM scm_axis) return ly_interval2scm (e); } - Interval -robust_relative_extent (Grob*me, Grob*refp, Axis a) +robust_relative_extent (Grob *me, Grob *refp, Axis a) { - Interval ext = me->extent (refp, a); - if (ext.is_empty()) + Interval ext = me->extent (refp, a); + if (ext.is_empty ()) { ext.add_point (me->relative_coordinate (refp, a)); } - return ext; + return ext; } Output_def * -Grob::get_paper () const +Grob::get_layout () const { - return pscore_ ? pscore_->paper_ : 0; + return pscore_ ? pscore_->layout () : 0; } - /* Recursively track all dependencies of this Grob. The status_ field is used as a mark-field. It is marked with BUSY during execution of this function, and marked with FINAL when finished. @@ -170,15 +188,15 @@ Grob::calculate_dependencies (int final, int busy, SCM funcname) if (status_ == busy) { - programming_error ("Element is busy, come back later"); + programming_error ("element is busy, come back later"); return; } status_ = busy; - for (SCM d = get_property ("dependencies"); ly_c_pair_p (d); - d = ly_cdr (d)) - unsmob_grob (ly_car (d))->calculate_dependencies (final, busy, funcname); + for (SCM d = get_property ("dependencies"); scm_is_pair (d); + d = scm_cdr (d)) + unsmob_grob (scm_car (d))->calculate_dependencies (final, busy, funcname); SCM proc = internal_get_property (funcname); if (ly_c_procedure_p (proc)) @@ -188,7 +206,7 @@ Grob::calculate_dependencies (int final, int busy, SCM funcname) } Stencil * -Grob::get_stencil () const +Grob::get_stencil () const { if (!is_live ()) return 0; @@ -198,10 +216,9 @@ Grob::get_stencil () const return unsmob_stencil (stil); stil = get_uncached_stencil (); - if (is_live ()) { - Grob *me = (Grob*) this; + Grob *me = (Grob *) this; me->set_property ("stencil", stil); } @@ -213,29 +230,42 @@ Grob::get_uncached_stencil () const { SCM proc = get_property ("print-function"); - SCM stil = SCM_EOL; + SCM stil = SCM_EOL; if (ly_c_procedure_p (proc)) stil = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED)); if (Stencil *m = unsmob_stencil (stil)) - { + { if (to_boolean (get_property ("transparent"))) stil = Stencil (m->extent_box (), SCM_EOL).smobbed_copy (); else { - SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm(), + SCM expr = m->expr (); + if (point_and_click_global) + expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm (), expr); + + stil = Stencil (m->extent_box (), expr).smobbed_copy (); + } + + /* color support... see interpret_stencil_expression () for more... */ + SCM color = get_property ("color"); + if (color != SCM_EOL) + { + m = unsmob_stencil (stil); + SCM expr = scm_list_3 (ly_symbol2scm ("color"), + color, m->expr ()); - stil = Stencil (m->extent_box (),expr). smobbed_copy (); + + stil = Stencil (m->extent_box (), expr).smobbed_copy (); } - } + } return stil; } /* VIRTUAL STUBS - - */ +*/ void Grob::do_break_processing () { @@ -251,16 +281,15 @@ void Grob::add_dependency (Grob *e) { if (e) - Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"), - e); + Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"), e); else - programming_error ("Null dependency added"); + programming_error ("null dependency added"); } void Grob::handle_broken_dependencies () { - Spanner *sp = dynamic_cast (this); + Spanner *sp = dynamic_cast (this); if (original_ && sp) return; @@ -268,8 +297,8 @@ Grob::handle_broken_dependencies () /* THIS, SP is the original spanner. We use a special function because some Spanners have enormously long lists in their properties, and a special function fixes FOO */ - for (SCM s = mutable_property_alist_; ly_c_pair_p (s); s = ly_cdr (s)) - sp->substitute_one_mutable_property (ly_caar (s), ly_cdar (s)); + for (SCM s = mutable_property_alist_; scm_is_pair (s); s = scm_cdr (s)) + sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s)); System *system = get_system (); @@ -279,7 +308,7 @@ Grob::handle_broken_dependencies () substitute_mutable_properties (system ? system->self_scm () : SCM_UNDEFINED, mutable_property_alist_); - else if (dynamic_cast (this)) + else if (dynamic_cast (this)) substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_); else /* THIS element is `invalid'; it has been removed from all @@ -307,6 +336,9 @@ Grob::suicide () set_extent (SCM_EOL, Y_AXIS); set_extent (SCM_EOL, X_AXIS); + set_extent_callback (SCM_EOL, Y_AXIS); + set_extent_callback (SCM_EOL, X_AXIS); + for (int a = X_AXIS; a <= Y_AXIS; a++) { dim_cache_[a].offset_callbacks_ = SCM_EOL; @@ -321,7 +353,7 @@ Grob::handle_prebroken_dependencies () mutable_property_alist_ centralized. */ if (original_) { - Item *it = dynamic_cast (this); + Item *it = dynamic_cast (this); substitute_mutable_properties (scm_int2num (it->break_status_dir ()), original_->mutable_property_alist_); } @@ -343,7 +375,6 @@ Grob::translate_axis (Real y, Axis a) dim_cache_[a].offset_ += y; } - /* Find the offset relative to D. If D equals THIS, then it is 0. Otherwise, it recursively defd as @@ -366,14 +397,14 @@ Grob::relative_coordinate (Grob const *refp, Axis a) const Real Grob::get_offset (Axis a) const { - Grob *me = (Grob*) this; + Grob *me = (Grob *) this; while (dim_cache_[a].offsets_left_) { int l = --me->dim_cache_[a].offsets_left_; SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_int2num (l)); SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a)); - Real r = scm_to_double (retval); + Real r = scm_to_double (retval); if (isinf (r) || isnan (r)) { programming_error (INFINITY_MSG); @@ -387,8 +418,22 @@ Grob::get_offset (Axis a) const bool Grob::is_empty (Axis a) const { - return ! (ly_c_pair_p (dim_cache_[a].dimension_) - || ly_c_procedure_p (dim_cache_[a].dimension_)); + return !(scm_is_pair (dim_cache_[a].dimension_) + || ly_c_procedure_p (dim_cache_[a].dimension_callback_)); +} + +void +Grob::flush_extent_cache (Axis axis) +{ + Dimension_cache *d = &dim_cache_[axis]; + if (ly_c_procedure_p (d->dimension_callback_) + && scm_is_pair (d->dimension_)) + { + d->dimension_ = SCM_EOL; + + if (get_parent (axis)) + get_parent (axis)->flush_extent_cache (axis); + } } Interval @@ -396,17 +441,19 @@ Grob::extent (Grob *refp, Axis a) const { Real x = relative_coordinate (refp, a); - Dimension_cache *d = (Dimension_cache *) &dim_cache_[a]; + Dimension_cache *d = (Dimension_cache *) & dim_cache_[a]; Interval ext; - if (ly_c_pair_p (d->dimension_)) + + SCM dimpair = d->dimension_; + if (scm_is_pair (dimpair)) ; - else if (ly_c_procedure_p (d->dimension_)) - /* FIXME: add doco on types, and should typecheck maybe? */ - d->dimension_= scm_call_2 (d->dimension_, self_scm (), scm_int2num (a)); + else if (ly_c_procedure_p (d->dimension_callback_) + && d->dimension_ == SCM_EOL) + d->dimension_ = scm_call_2 (d->dimension_callback_, self_scm (), scm_int2num (a)); else return ext; - if (!ly_c_pair_p (d->dimension_)) + if (!scm_is_pair (d->dimension_)) return ext; ext = ly_scm2interval (d->dimension_); @@ -416,18 +463,18 @@ Grob::extent (Grob *refp, Axis a) const : "extra-Y-extent"); /* Signs ? */ - if (ly_c_pair_p (extra)) + if (scm_is_pair (extra)) { - ext[BIGGER] += scm_to_double (ly_cdr (extra)); - ext[SMALLER] += scm_to_double (ly_car (extra)); + ext[BIGGER] += scm_to_double (scm_cdr (extra)); + ext[SMALLER] += scm_to_double (scm_car (extra)); } extra = get_property (a == X_AXIS ? "minimum-X-extent" : "minimum-Y-extent"); - if (ly_c_pair_p (extra)) - ext.unite (Interval (scm_to_double (ly_car (extra)), - scm_to_double (ly_cdr (extra)))); + if (scm_is_pair (extra)) + ext.unite (Interval (scm_to_double (scm_car (extra)), + scm_to_double (scm_cdr (extra)))); ext.translate (x); @@ -444,7 +491,7 @@ Grob::common_refpoint (Grob const *s, Axis a) const for (Grob const *c = this; c; c = c->dim_cache_[a].parent_) for (Grob const *d = s; d; d = d->dim_cache_[a].parent_) if (d == c) - return (Grob*) d; + return (Grob *) d; return 0; } @@ -452,8 +499,8 @@ Grob::common_refpoint (Grob const *s, Axis a) const Grob * common_refpoint_of_list (SCM elist, Grob *common, Axis a) { - for (; ly_c_pair_p (elist); elist = ly_cdr (elist)) - if (Grob *s = unsmob_grob (ly_car (elist))) + for (; scm_is_pair (elist); elist = scm_cdr (elist)) + if (Grob *s = unsmob_grob (scm_car (elist))) { if (common) common = common->common_refpoint (s, a); @@ -467,7 +514,7 @@ common_refpoint_of_list (SCM elist, Grob *common, Axis a) Grob * common_refpoint_of_array (Link_array const &arr, Grob *common, Axis a) { - for (int i = arr.size (); i--; ) + for (int i = arr.size (); i--;) if (Grob *s = arr[i]) { if (common) @@ -484,7 +531,7 @@ Grob::name () const { SCM meta = get_property ("meta"); SCM nm = scm_assoc (ly_symbol2scm ("name"), meta); - nm = (ly_c_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL; + nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL; return scm_is_symbol (nm) ? ly_symbol2string (nm) : classname (this); } @@ -492,21 +539,21 @@ void Grob::add_offset_callback (SCM cb, Axis a) { if (!has_offset_callback (cb, a)) - { - dim_cache_[a].offset_callbacks_ - = scm_cons (cb, dim_cache_[a].offset_callbacks_); - dim_cache_[a].offsets_left_ ++; - } + { + dim_cache_[a].offset_callbacks_ + = scm_cons (cb, dim_cache_[a].offset_callbacks_); + dim_cache_[a].offsets_left_++; + } } bool -Grob::has_extent_callback (SCM cb, Axis a)const +Grob::has_extent_callback (SCM cb, Axis a) const { - return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T; + return scm_equal_p (cb, dim_cache_[a].dimension_callback_) == SCM_BOOL_T; } bool -Grob::has_offset_callback (SCM cb, Axis a)const +Grob::has_offset_callback (SCM cb, Axis a) const { return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F; } @@ -517,18 +564,24 @@ Grob::set_extent (SCM dc, Axis a) dim_cache_[a].dimension_ = dc; } +void +Grob::set_extent_callback (SCM dc, Axis a) +{ + dim_cache_[a].dimension_callback_ = dc; +} + void Grob::set_parent (Grob *g, Axis a) { dim_cache_[a].parent_ = g; } -MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1); +MAKE_SCHEME_CALLBACK (Grob, fixup_refpoint, 1); SCM Grob::fixup_refpoint (SCM smob) { Grob *me = unsmob_grob (smob); - for (int a = X_AXIS; a < NO_AXES; a ++) + for (int a = X_AXIS; a < NO_AXES; a++) { Axis ax = (Axis)a; Grob *parent = me->get_parent (ax); @@ -542,14 +595,14 @@ Grob::fixup_refpoint (SCM smob) me->set_parent (newparent, ax); } - if (Item *i = dynamic_cast (me)) + if (Item *i = dynamic_cast (me)) { - Item *parenti = dynamic_cast (parent); + Item *parenti = dynamic_cast (parent); if (parenti && i) { - Direction my_dir = i->break_status_dir () ; - if (my_dir!= parenti->break_status_dir ()) + Direction my_dir = i->break_status_dir (); + if (my_dir != parenti->break_status_dir ()) { Item *newparent = parenti->find_prebroken_piece (my_dir); me->set_parent (newparent, ax); @@ -561,7 +614,7 @@ Grob::fixup_refpoint (SCM smob) } void -Grob::warning (String s)const +Grob::warning (String s) const { SCM cause = self_scm (); while (Grob *g = unsmob_grob (cause)) @@ -576,14 +629,13 @@ Grob::warning (String s)const void Grob::programming_error (String s) const { - s = "Programming error: " + s; - warning (s); + s = _f ("programming error: %s", s); + message (s); } - /**************************************************** SMOB funcs - ****************************************************/ +****************************************************/ IMPLEMENT_SMOBS (Grob); IMPLEMENT_DEFAULT_EQUAL_P (Grob); @@ -591,13 +643,14 @@ IMPLEMENT_DEFAULT_EQUAL_P (Grob); SCM Grob::mark_smob (SCM ses) { - Grob *s = (Grob*) SCM_CELL_WORD_1 (ses); + Grob *s = (Grob *) SCM_CELL_WORD_1 (ses); scm_gc_mark (s->immutable_property_alist_); - - for (int a = 0 ; a < 2; a++) + scm_gc_mark (s->key_->self_scm ()); + for (int a = 0; a < 2; a++) { scm_gc_mark (s->dim_cache_[a].offset_callbacks_); scm_gc_mark (s->dim_cache_[a].dimension_); + scm_gc_mark (s->dim_cache_[a].dimension_callback_); /* Do not mark the parents. The pointers in the mutable property list form two tree like structures (one for X @@ -610,6 +663,9 @@ Grob::mark_smob (SCM ses) if (s->original_) scm_gc_mark (s->original_->self_scm ()); + if (s->pscore_) + scm_gc_mark (s->pscore_->self_scm ()); + s->do_derived_mark (); return s->mutable_property_alist_; } @@ -617,7 +673,7 @@ Grob::mark_smob (SCM ses) int Grob::print_smob (SCM s, SCM port, scm_print_state *) { - Grob *sc = (Grob *) ly_cdr (s); + Grob *sc = (Grob *) SCM_CELL_WORD_1 (s); scm_puts ("#name ().to_str0 (), port); @@ -646,15 +702,21 @@ Grob::internal_has_interface (SCM k) return scm_c_memq (k, ifs) != SCM_BOOL_F; } +Grob * +Grob::get_parent (Axis a) const +{ + return dim_cache_[a].parent_; +} + /** Return Array of Grobs in SCM list LST */ Link_array ly_scm2grobs (SCM lst) { Link_array arr; - for (SCM s = lst; ly_c_pair_p (s); s = ly_cdr (s)) + for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s)) { - SCM e = ly_car (s); + SCM e = scm_car (s); arr.push (unsmob_grob (e)); } @@ -662,40 +724,45 @@ ly_scm2grobs (SCM lst) return arr; } +Object_key const * +Grob::get_key () const +{ + return key_; +} + /** Return SCM list of Grob array A */ SCM ly_grobs2scm (Link_array a) { SCM s = SCM_EOL; for (int i = a.size (); i; i--) - s = scm_cons (a[i-1]->self_scm (), s); + s = scm_cons (a[i - 1]->self_scm (), s); return s; } - IMPLEMENT_TYPE_P (Grob, "ly:grob?"); ADD_INTERFACE (Grob, "grob-interface", "A grob represents a piece of music notation\n" "\n" -"All grobs have an X and Y-position on the page. These X and Y positions\n" -"are stored in a relative format, so they can easily be combined by\n" -"stacking them, hanging one grob to the side of another, and coupling\n" -"them into a grouping objects.\n" -"\n" -"Each grob has a reference point (a.k.a. parent): the position of a grob\n" -"is stored relative to that reference point. For example the X-reference\n" -"point of a staccato dot usually is the note head that it applies\n" -"to. When the note head is moved, the staccato dot moves along\n" -"automatically.\n" -"\n" -"A grob is often associated with a symbol, but some grobs do not print\n" -"any symbols. They take care of grouping objects. For example, there is a\n" -"separate grob that stacks staves vertically. The @ref{NoteCollision}\n" -"is also an abstract grob: it only moves around chords, but doesn't print\n" -"anything.\n" -"\n" + "All grobs have an X and Y-position on the page. These X and Y positions\n" + "are stored in a relative format, so they can easily be combined by\n" + "stacking them, hanging one grob to the side of another, and coupling\n" + "them into a grouping objects.\n" + "\n" + "Each grob has a reference point (a.k.a. parent): the position of a grob\n" + "is stored relative to that reference point. For example the X-reference\n" + "point of a staccato dot usually is the note head that it applies\n" + "to. When the note head is moved, the staccato dot moves along\n" + "automatically.\n" + "\n" + "A grob is often associated with a symbol, but some grobs do not print\n" + "any symbols. They take care of grouping objects. For example, there is a\n" + "separate grob that stacks staves vertically. The @ref{NoteCollision}\n" + "is also an abstract grob: it only moves around chords, but doesn't print\n" + "anything.\n" + "\n" "Grobs have a properties: Scheme variables, that can be read and set. " "They have two types. Immutable variables " "define the default style and behavior. They are shared between many objects. " @@ -704,16 +771,14 @@ ADD_INTERFACE (Grob, "grob-interface", "Mutable properties are variables that are specific to one grob. Typically, " "lists of other objects, or results from computations are stored in" "mutable properties: every call to set-grob-property (or its C++ equivalent) " - "sets a mutable property. " - -, + "sets a mutable property. ", "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause " "Y-extent-callback print-function extra-offset spacing-procedure " - "staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent " + "context staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent " "meta layer before-line-breaking-callback " + "color " + "axis-group-parent-X " + "axis-group-parent-Y " "after-line-breaking-callback extra-Y-extent minimum-X-extent " - // FIXME: page-penalty? - "minimum-Y-extent page-penalty transparent " - ); - + "minimum-Y-extent transparent tweak-count tweak-rank");