PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/lily/break-substitution.cc

https://gitlab.com/aguai/lilypond
C++ | 534 lines | 330 code | 62 blank | 142 comment | 66 complexity | a7298f5d00ed35e89213bfba5b828cad MD5 | raw file
  1. /*
  2. This file is part of LilyPond, the GNU music typesetter.
  3. Copyright (C) 2001--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
  4. LilyPond is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. LilyPond is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include <cstdio>
  16. #include <cstdlib>
  17. using namespace std;
  18. #include "item.hh"
  19. #include "system.hh"
  20. #include "grob-array.hh"
  21. static SCM break_criterion;
  22. void
  23. set_break_subsititution (SCM criterion)
  24. {
  25. break_criterion = criterion;
  26. }
  27. /*
  28. Perform the substitution for a single grob.
  29. */
  30. Grob *
  31. substitute_grob (Grob *sc)
  32. {
  33. if (scm_is_integer (break_criterion))
  34. {
  35. Item *i = dynamic_cast<Item *> (sc);
  36. Direction d = to_dir (break_criterion);
  37. if (i && i->break_status_dir () != d)
  38. {
  39. Item *br = i->find_prebroken_piece (d);
  40. return br;
  41. }
  42. }
  43. else
  44. {
  45. System *line
  46. = dynamic_cast<System *> (unsmob_grob (break_criterion));
  47. if (sc->get_system () != line)
  48. sc = sc->find_broken_piece (line);
  49. /* now: !sc || (sc && sc->get_system () == line) */
  50. if (!sc)
  51. return 0;
  52. /* now: sc && sc->get_system () == line */
  53. if (!line)
  54. return sc;
  55. /*
  56. We don't return SCM_UNDEFINED for
  57. suicided grobs, for two reasons
  58. - it doesn't work (strange disappearing objects)
  59. - it forces us to mark the parents of a grob, leading to
  60. a huge recursion in the GC routine.
  61. */
  62. if (sc->common_refpoint (line, X_AXIS)
  63. && sc->common_refpoint (line, Y_AXIS))
  64. return sc;
  65. return 0;
  66. }
  67. return sc;
  68. }
  69. /*
  70. Do break substitution in S, using CRITERION. Return new value.
  71. CRITERION is either a SMOB pointer to the desired line, or a number
  72. representing the break direction. Do not modify SRC.
  73. It is rather tightly coded, since it takes a lot of time; it is
  74. one of the top functions in the profile.
  75. We don't pass break_criterion as a parameter, since it is
  76. `constant', but takes up stack space.
  77. It would be nice if we could do this in-place partially. We now
  78. generate a lot of garbage.
  79. */
  80. SCM
  81. do_break_substitution (SCM src)
  82. {
  83. again:
  84. if (unsmob_grob (src))
  85. {
  86. Grob *new_ptr = substitute_grob (unsmob_grob (src));
  87. return new_ptr ? new_ptr->self_scm () : SCM_UNDEFINED;
  88. }
  89. else if (scm_is_vector (src))
  90. {
  91. int len = scm_c_vector_length (src);
  92. SCM nv = scm_c_make_vector (len, SCM_UNDEFINED);
  93. for (int i = 0; i < len; i++)
  94. {
  95. SCM si = scm_from_int (i);
  96. scm_vector_set_x (nv, si,
  97. do_break_substitution (scm_vector_ref (src, si)));
  98. }
  99. }
  100. else if (scm_is_pair (src))
  101. {
  102. /*
  103. UGH! breaks on circular lists.
  104. */
  105. SCM newcar = do_break_substitution (scm_car (src));
  106. SCM oldcdr = scm_cdr (src);
  107. if (newcar == SCM_UNDEFINED
  108. && (scm_is_pair (oldcdr) || oldcdr == SCM_EOL))
  109. {
  110. /*
  111. This is tail-recursion, ie.
  112. return do_break_substution (cdr);
  113. We don't want to rely on the compiler to do this. Without
  114. tail-recursion, this easily crashes with a stack overflow. */
  115. src = oldcdr;
  116. goto again;
  117. }
  118. return scm_cons (newcar, do_break_substitution (oldcdr));
  119. }
  120. else
  121. return src;
  122. return src;
  123. }
  124. /*
  125. Perform substitution on GROB_LIST using a constant amount of stack.
  126. */
  127. vector<Grob *> temporary_substition_array;
  128. void
  129. substitute_grob_array (Grob_array *grob_arr, Grob_array *new_arr)
  130. {
  131. vector<Grob *> &old_grobs (grob_arr->array_reference ());
  132. vector<Grob *> *new_grobs (new_arr == grob_arr
  133. ? & temporary_substition_array
  134. : &new_arr->array_reference ());
  135. new_grobs->resize (old_grobs.size ());
  136. Grob **array = (Grob **) new_grobs->data ();
  137. Grob **ptr = array;
  138. for (vsize i = 0; i < old_grobs.size (); i++)
  139. {
  140. Grob *orig = old_grobs[i];
  141. Grob *new_grob = substitute_grob (orig);
  142. if (new_grob)
  143. *ptr++ = new_grob;
  144. }
  145. new_grobs->resize (ptr - array);
  146. if (new_arr == grob_arr)
  147. new_arr->set_array (*new_grobs);
  148. }
  149. /*
  150. We don't do
  151. forall b in broken-childs:
  152. forall p in properties:
  153. forall g in p (if grob-list):
  154. g := substitute (g)
  155. for spanners since this is O (SYSTEMCOUNT * GROBCOUNT), and SYSTEMCOUNT =
  156. O (GROBCOUNT), we have a quadratic algorithm. --for a single spanner
  157. This is problematic: with large (long) scores, the costs can be
  158. significant; especially all-elements in System, can become huge. For
  159. a typical 50 page score, it requires running through a 100k list 50
  160. times.
  161. Instead:
  162. forall p in properties:
  163. (if grob list)
  164. put grob list in array,
  165. reorder array so spanners are separate -- O (grobcount)
  166. find first and last indexes of grobs on a specific system
  167. for items this is O (itemcount)
  168. for spanners this is O (sum-of spanner-system-ranges)
  169. perform the substitution O (sum-of spanner-system-ranges)
  170. The complexity is harder to determine, but should be subquadratic;
  171. For the situation above, we run through the entire 100k list once,
  172. and also (more or less) once through the item part of the 100k (say
  173. 98k elements) of the list.
  174. These timings were measured without -O2.
  175. lehre, before 28.98 seconds, after: 27.91 seconds, 3.5 %.
  176. coriolan, before 2:30, after: 1:59. Increase of 20%.
  177. moz-k498-p1, before 24.10, after: 19.790s, Increase of 18%
  178. */
  179. Slice
  180. spanner_system_range (Spanner *sp)
  181. {
  182. Slice rv;
  183. if (System *st = sp->get_system ())
  184. rv = Slice (st->get_rank (), st->get_rank ());
  185. else
  186. {
  187. if (sp->broken_intos_.size ())
  188. rv = Slice (sp->broken_intos_[0]->get_system ()->get_rank (),
  189. sp->broken_intos_.back ()->get_system ()->get_rank ());
  190. }
  191. return rv;
  192. }
  193. Slice
  194. item_system_range (Item *it)
  195. {
  196. if (System *st = it->get_system ())
  197. return Slice (st->get_rank (), st->get_rank ());
  198. Slice sr;
  199. for (LEFT_and_RIGHT (d))
  200. {
  201. Item *bi = it->find_prebroken_piece (d);
  202. if (bi && bi->get_system ())
  203. sr.add_point (bi->get_system ()->get_rank ());
  204. }
  205. return sr;
  206. }
  207. Slice
  208. grob_system_range (Grob *g)
  209. {
  210. if (Spanner *s = dynamic_cast<Spanner *> (g))
  211. return spanner_system_range (s);
  212. else if (Item *it = dynamic_cast<Item *> (g))
  213. return item_system_range (it);
  214. else
  215. return Slice ();
  216. }
  217. struct Substitution_entry
  218. {
  219. Grob *grob_;
  220. /* Assumption: we have less than 32k paper columns. */
  221. short left_;
  222. short right_;
  223. void set (Grob *g, Slice sr)
  224. {
  225. grob_ = g;
  226. /*
  227. duh, don't support scores with more than 32000 systems.
  228. */
  229. if (sr.is_empty ())
  230. {
  231. /*
  232. overflow if we don't treat this specially.
  233. */
  234. left_ = 1;
  235. right_ = -1;
  236. }
  237. else
  238. {
  239. left_ = (short) sr[LEFT];
  240. right_ = (short) sr[RIGHT];
  241. }
  242. }
  243. Substitution_entry ()
  244. {
  245. grob_ = 0;
  246. left_ = right_ = -2;
  247. }
  248. int length () { return right_ - left_; }
  249. static int
  250. item_compare (void const *a, void const *b)
  251. {
  252. return ((Substitution_entry *)a)->left_
  253. - ((Substitution_entry *)b)->left_;
  254. }
  255. static int
  256. spanner_compare (void const *a, void const *b)
  257. {
  258. return ((Substitution_entry *)a)->length ()
  259. - ((Substitution_entry *)b)->length ();
  260. }
  261. };
  262. bool
  263. Spanner::fast_substitute_grob_array (SCM sym,
  264. Grob_array *grob_array)
  265. {
  266. int len = grob_array->size ();
  267. if (grob_array->ordered ())
  268. return false;
  269. if (len < 15)
  270. return false;
  271. /*
  272. We store items on the left, spanners on the right in this vector.
  273. FIXME: will not multithread.
  274. */
  275. static Substitution_entry *vec;
  276. static int vec_room;
  277. if (vec_room < len)
  278. {
  279. vec = (Substitution_entry *) realloc (vec, sizeof (Substitution_entry) * len);
  280. vec_room = len;
  281. }
  282. Slice system_range = spanner_system_range (this);
  283. int spanner_index = len;
  284. int item_index = 0;
  285. for (vsize i = 0; i < grob_array->size (); i++)
  286. {
  287. Grob *g = grob_array->grob (i);
  288. Slice sr = grob_system_range (g);
  289. sr.intersect (system_range);
  290. int idx = 0;
  291. if (dynamic_cast<Spanner *> (g))
  292. idx = --spanner_index;
  293. else if (dynamic_cast<Item *> (g))
  294. idx = item_index++;
  295. vec[idx].set (g, sr);
  296. }
  297. qsort (vec, item_index,
  298. sizeof (Substitution_entry), &Substitution_entry::item_compare);
  299. vector<Slice> item_indices;
  300. vector<Slice> spanner_indices;
  301. for (int i = 0; i <= system_range.length (); i++)
  302. {
  303. item_indices.push_back (Slice (len, 0));
  304. spanner_indices.push_back (Slice (len, 0));
  305. }
  306. vector<Slice> *arrs[]
  307. =
  308. {
  309. &item_indices, &spanner_indices
  310. };
  311. for (int i = 0; i < item_index; i++)
  312. {
  313. for (int j = vec[i].left_; j <= vec[i].right_; j++)
  314. item_indices[j - system_range[LEFT]].add_point (i);
  315. }
  316. /*
  317. sorting vec[spanner_index.. len]
  318. is a waste of time -- the staff-spanners screw up the
  319. ordering, since they go across the entire score.
  320. */
  321. for (vsize i = spanner_indices.size (); i--;)
  322. spanner_indices[i] = Slice (spanner_index, len - 1);
  323. assert (item_index <= spanner_index);
  324. assert ((broken_intos_.size () == (vsize)system_range.length () + 1)
  325. || (broken_intos_.empty () && system_range.length () == 0));
  326. for (vsize i = 0; i < broken_intos_.size (); i++)
  327. {
  328. Grob *sc = broken_intos_[i];
  329. System *l = sc->get_system ();
  330. set_break_subsititution (l ? l->self_scm () : SCM_UNDEFINED);
  331. SCM newval = sc->internal_get_object (sym);
  332. if (!unsmob_grob_array (newval))
  333. {
  334. newval = Grob_array::make_array ();
  335. sc->set_object (sym, newval);
  336. }
  337. Grob_array *new_array = unsmob_grob_array (newval);
  338. for (int k = 0; k < 2; k++)
  339. for (int j = (*arrs[k])[i][LEFT]; j <= (*arrs[k])[i][RIGHT]; j++)
  340. {
  341. Grob *substituted = substitute_grob (vec[j].grob_);
  342. if (substituted)
  343. new_array->add (substituted);
  344. }
  345. #ifdef PARANOIA
  346. printf ("%d (%d), sp %d (%d)\n",
  347. item_indices [i].length (), item_index,
  348. spanner_indices[i].length (), len - spanner_index);
  349. {
  350. SCM l1 = substitute_grob_list (grob_list);
  351. assert (scm_ilength (l1) == scm_ilength (newval));
  352. }
  353. #endif
  354. }
  355. return true;
  356. }
  357. /*
  358. Although the substitution can be written as
  359. property_alist = do_substitution (other_property_alist),
  360. we have a special function here: we want to invoke a special
  361. function for lists of grobs. These can be very long for large
  362. orchestral scores (eg. 1M elements). do_break_substitution () can
  363. recurse many levels, taking lots of stack space.
  364. This becomes a problem if lily is linked against guile with
  365. pthreads. pthreads impose small limits on the stack size.
  366. */
  367. SCM
  368. substitute_object_alist (SCM alist, SCM dest)
  369. {
  370. SCM l = SCM_EOL;
  371. SCM *tail = &l;
  372. for (SCM s = alist; scm_is_pair (s); s = scm_cdr (s))
  373. {
  374. SCM sym = scm_caar (s);
  375. SCM val = scm_cdar (s);
  376. if (Grob_array *orig = unsmob_grob_array (val))
  377. {
  378. SCM handle = scm_assq (sym, dest);
  379. SCM newval
  380. = (scm_is_pair (handle))
  381. ? scm_cdr (handle)
  382. : Grob_array::make_array ();
  383. Grob_array *new_arr = unsmob_grob_array (newval);
  384. substitute_grob_array (orig, new_arr);
  385. val = newval;
  386. }
  387. else
  388. val = do_break_substitution (val);
  389. if (val != SCM_UNDEFINED)
  390. {
  391. /*
  392. for ly:grob? properties, SCM_UNDEFINED could leak out
  393. through ly:grob-property
  394. */
  395. *tail = scm_cons (scm_cons (sym, val), SCM_EOL);
  396. tail = SCM_CDRLOC (*tail);
  397. }
  398. }
  399. return l;
  400. }
  401. void
  402. Spanner::substitute_one_mutable_property (SCM sym,
  403. SCM val)
  404. {
  405. Spanner *s = this;
  406. bool fast_done = false;
  407. Grob_array *grob_array = unsmob_grob_array (val);
  408. if (grob_array)
  409. fast_done = s->fast_substitute_grob_array (sym, grob_array);
  410. if (!fast_done)
  411. for (vsize i = 0; i < s->broken_intos_.size (); i++)
  412. {
  413. Grob *sc = s->broken_intos_[i];
  414. System *l = sc->get_system ();
  415. set_break_subsititution (l ? l->self_scm () : SCM_UNDEFINED);
  416. if (grob_array)
  417. {
  418. SCM newval = sc->internal_get_object (sym);
  419. if (!unsmob_grob_array (newval))
  420. {
  421. newval = Grob_array::make_array ();
  422. sc->set_object (sym, newval);
  423. }
  424. substitute_grob_array (grob_array, unsmob_grob_array (newval));
  425. }
  426. else
  427. {
  428. SCM newval = do_break_substitution (val);
  429. sc->set_object (sym, newval);
  430. }
  431. }
  432. }
  433. void
  434. Grob::substitute_object_links (SCM crit, SCM orig)
  435. {
  436. set_break_subsititution (crit);
  437. object_alist_ = substitute_object_alist (orig, object_alist_);
  438. }