--- /dev/null
+#ifndef PROBLEM_HH
+#define PROBLEM_HH
+
+#include "glob.hh"
+#include "plist.hh"
+#include "vray.hh"
+#include "pcol.hh"
+#include "matrix.hh"
+
+/// helper struct for #Spacing_problem#
+struct Colinfo {
+ const PCol *pcol_;
+ bool fixed;
+ Real fixpos;
+ Colinfo();
+ void print() const;
+ Real minright() const { return pcol_->width().max; }
+ Real minleft() const { return -pcol_->width().min; }
+};
+
+
+/// spacing for one line.
+class Spacing_problem {
+ svec<const Idealspacing*> ideals;
+ svec<Colinfo> cols;
+
+ /// the index of #c# in #cols#
+ int col_id(const PCol *c) const;
+
+ /// generate an (nonoptimal) solution
+ Vector find_initial_solution() const;
+
+ /// check if problem is too tight
+ bool check_feasible() const;
+ /// does #this# contain the column #w#?
+ bool contains(const PCol *w);
+
+ /// make the energy function
+ void make_matrices(Matrix &quad, Vector &lin,Real&) const;
+
+ /// generate the LP constraints
+ void make_constraints(Mixed_qp& lp) const;
+
+public:
+ /// solve the spacing problem
+ svec<Real> solve() const;
+ /**
+ return the column positions, and the energy (last element)
+ */
+ /// add a idealspacing to the problem.
+ void add_ideal(const Idealspacing *i);
+
+ /**
+ One pair of columns can have no, one or more idealspacings,
+ since they can be "summed" if the columns to which #i# refers are
+ not in this problem, the spacing is ignored.
+ */
+
+
+ /// add a col to the problem
+ void add_column(const PCol *, bool fixed=false, Real fixpos=0.0);
+ /** columns have to be added left to right. The column contains
+ info on it's minimum width.
+ */
+
+
+ bool check_constraints(Vector v) const;
+
+ Vector try_initial_solution() const;
+ void OK() const;
+ void print() const;
+ void print_ideal(const Idealspacing*)const;
+};
+
+
+/** the problem, given by the columns (which include constraints) and
+ intercolumn spacing. The problem is:
+
+ Generate a spacing which
+ \begin{itemize}
+ \item
+ Satisfies spacing constraints (notes can't be printed through each other)
+ \item
+ Looks good, ie tries to conform to an ideal spacing as much as possible.
+ \end{itemize}
+ This is converted by regarding idealspacing as "springs" attached
+ to columns. The equilibrium of one spring is the ideal
+ distance. The columns have a size, this imposes "hard" constraints
+ on the distances. This transforms the problem into a quadratic
+ programming problem with linear constraints.
+
+ The quality is given by the total potential energy in the
+ springs. The lower the energy, the better the configuration.
+*/
+#endif
--- /dev/null
+// the breaking problem for a score.
+
+#ifndef PSCORE_HH
+#define PSCORE_HH
+
+
+#include "vray.hh"
+#include "pcol.hh"
+#include "pstaff.hh"
+
+/// all stuff which goes onto paper
+struct PScore {
+ Paperdef *paper_; // indirection.
+
+ /// the columns, ordered left to right
+ PointerList<PCol *> cols;
+
+ /// the idealspacings, no particular order
+ PointerList<Idealspacing*> suz;
+
+ /// the staffs ordered top to bottom
+ PointerList<PStaff*> staffs;
+
+ /// all symbols in score. No particular order.
+ PointerList<Item*> its;
+
+ /// if broken, the different lines
+ PointerList<Line_of_score*> lines;
+
+ /// crescs etc; no particular order
+ PointerList<Spanner *> spanners;
+
+ /****************************************************************/
+
+ svec<Item*> select_items(PStaff*, PCol*);
+
+ /// before calc_breaking
+ void preprocess();
+
+ void calc_breaking();
+ /**
+ calculate where the lines are to be broken.
+
+ POST
+
+ lines contain the broken lines.
+ */
+
+ /// after calc_breaking
+ void postprocess();
+
+ /// search all pcols which are breakable.
+ svec<const PCol *> find_breaks() const;
+
+ /// add a line to the broken stuff. Positions given in #config#
+ void add_line(svec<const PCol *> curline, svec<Real> config);
+
+ /// helper: solve for the columns in #curline#.
+ svec<Real> solve_line(svec<const PCol *> curline) const;
+
+ void add(PStaff *);
+ /// add item
+ void typeset_item(Item *, PCol *,PStaff*,int=1);
+
+ /// add an Spanner
+ void typeset_spanner(Spanner*, PStaff*);
+
+ /// add to bottom of pcols
+ void add(PCol*);
+ /**
+
+ */
+ void output(Tex_stream &ts);
+
+ Idealspacing* get_spacing(PCol *, PCol *);
+ /*
+ get the spacing between c1 and c2, create one if necessary.
+ */
+
+ /// return argument as a cursor.
+ PCursor<PCol *> find_col(PCol *);
+
+ /// delete unused columns
+ void clean_cols();
+
+
+ /// check if the spacing/breaking problem is well-stated
+ void problem_OK() const;
+
+ /// invarinants
+ void OK()const;
+ PScore(Paperdef*);
+ void print() const;
+
+ /// does curline fit on the paper?
+ bool feasible(svec<const PCol *> curline) const;
+};
+/** notes, signs, symbols in a score can be grouped in two ways:
+ horizontally (staffwise), and vertically (columns). #PScore#
+ contains the items, the columns and the staffs.
+ */
+#endif
--- /dev/null
+/*
+ do calculations for breaking problem
+
+ */
+#include "paper.hh"
+#include "linespace.hh"
+#include "debug.hh"
+#include "scoreline.hh"
+#include "pscore.hh"
+
+// construct an appropriate Spacing_problem and solve it.
+svec<Real>
+PScore::solve_line(svec<const PCol *> curline) const
+{
+ Spacing_problem sp;
+
+ sp.add_column(curline[0], true, 0.0);
+ for (int i=1; i< curline.sz()-1; i++)
+ sp.add_column(curline[i]);
+ sp.add_column(curline.last(), true, paper_->linewidth);
+
+ // misschien moeven uit Spacing_problem?
+ for (PCursor<Idealspacing *> i(suz); i.ok(); i++) {
+ sp.add_ideal(i);
+ }
+ svec<Real> the_sol=sp.solve();
+ return the_sol;
+}
+
+bool
+PScore::feasible(svec<const PCol *> curline) const
+{
+ Real l =0;
+ for (int i=0; i < curline.sz(); i++)
+ l +=curline[i]->width().length();
+ return l < paper_->linewidth;
+}
+
+void
+PScore::problem_OK() const
+{
+ if (!cols.size())
+ error("PScore::problem_OK(): Score does not have any columns");
+ PCursor<PCol *> start(cols);
+ PCursor<PCol *> end (((PScore*)this)->cols.bottom());
+
+ assert(start->breakable());
+ assert(end->breakable());
+}
+
+struct Col_configuration {
+ svec<const PCol*> line;
+ svec<Real> config;
+ Real energy;
+
+ Col_configuration() {
+ energy = INFTY;
+ }
+ void add(const PCol*c) { line.add(c);}
+ void setsol(svec<Real> sol) {
+ config = sol;
+ energy = config.last();
+ config.pop();
+ }
+ void print() const {
+#ifndef NPRINT
+ mtor << "energy : " << energy << '\n';
+ mtor << "line of " << config.sz() << " cols\n";
+#endif
+ }
+};
+
+/// wordwrap type algorithm
+/* el stupido. This should be done more accurately:
+
+ It would be nice to have a Dynamic Programming type of algorithm
+ similar to TeX's
+
+ */
+
+void
+PScore::calc_breaking()
+{
+ OK();
+ problem_OK();
+ PCursor<PCol *> curcol(cols);
+
+ svec<const PCol *> breakpoints(find_breaks());
+ assert(breakpoints.sz()>=2);
+ for (int i=0 ; i < breakpoints.sz() -1; ) {
+ Col_configuration minimum;
+ Col_configuration current;
+
+ // do another line
+ PCol *post = breakpoints[i]->postbreak;
+ current.add( post);
+ curcol++; // skip the breakable.
+ i++;
+
+ while (i < breakpoints.sz()) {
+
+ // add another measure.
+ while(breakpoints[i] !=curcol){
+
+ current.add(curcol);
+ curcol++;
+ }
+ current.add(breakpoints[i]->prebreak );
+ if (!feasible(current.line)) {
+ if (!minimum.line.sz())
+ error("sorry, this measure is too long");
+ break;
+ }
+ current.setsol(solve_line(current.line));
+ current.print();
+
+ if (current.energy < minimum.energy) {
+ minimum = current;
+ } else { // we're one col too far.
+ i--;
+ while (curcol != breakpoints[i])
+ curcol --;
+
+ break;
+ }
+
+ current.line.last()=breakpoints[i];
+ curcol ++;
+ i++;
+ }
+ mtor << "Adding line, next breakpoint " << i << '\n';
+ add_line(minimum.line, minimum.config);
+ }
+}
+
--- /dev/null
+#include <ctype.h>
+
+#include "string.hh"
+#include "real.hh"
+#include "debug.hh"
+#include "request.hh"
+#include "voice.hh"
+#include "notename.hh"
+#include "vray.hh"
+
+int default_duration = 4, default_dots=0, default_octave=0;
+
+void
+parse_duration(const char *a, int &j, int &intdur, int &dots)
+{
+ String durstr;
+ while (isdigit(a[j]))
+ {
+ durstr += a[j++];
+ }
+
+ dots=default_dots;
+
+ while (a[j] == '.')
+ {
+ j++;
+ dots++;
+ }
+
+ intdur = (durstr.len()) ?
+ durstr.value():default_duration;
+
+ mtor << "dur " << intdur << "dots " << dots<<eol;
+}
+
+
+
+void
+parse_pitch( const char *a, int &j, int &oct, bool & overide_acc,
+ int & large, int & small)
+{
+ // octave
+ oct =default_octave;
+
+ while (1)
+ {
+ if (a[j] == '\'')
+ oct ++;
+ else if (a[j] == '`')
+ oct --;
+ else
+ break;
+ j++;
+
+ }
+
+ mtor << "oct " << oct;
+
+ // accidental
+ overide_acc = false;
+
+ if (a[j] == '!')
+ {
+ overide_acc = true;
+ j++;
+ }
+
+
+ // notename.
+ String nm;
+ while (isalpha(a[j]))
+ {
+ nm += a[j++];
+ }
+ if (isupper(nm[0]))
+ {
+ oct--;
+ nm.lower();
+ }
+
+
+ lookup_notename(large,small,nm);
+ mtor << "override: " << overide_acc;
+ mtor << "pitch "<< large <<", "<<small<<"\n";
+}
+
+
+Voice_element *
+get_note_element(String pitch, String durstr)
+{
+ Voice_element*v = new Voice_element;
+ int i=0;
+
+ int dur, dots;
+ parse_duration(durstr, i, dur, dots);
+ i=0;
+
+ Note_req * rq = new Note_req;
+
+ if (dur >= 2) {
+ Stem_req * st = new Stem_req(dur);
+ v->add(st);
+ }
+
+ int oct, pit, acc;
+ bool forceacc;
+ parse_pitch(pitch, i, oct, forceacc, pit, acc);
+ char nm = pit + 'c';
+ if (nm > 'g')
+ nm += 'a' - 'h';
+ rq->name =nm;
+
+ rq->octave = oct;
+ rq->accidental = acc;
+ rq->forceacc = forceacc;
+ rq->balltype = dur;
+ rq->dots = dots;
+
+ rq->print();
+
+ v->add(rq);
+
+ return v;
+}
+
+Voice_element *
+get_rest_element(String, String durstr)
+{
+ Voice_element*v = new Voice_element;
+ int i=0;
+
+ int dur, dots;
+ parse_duration(durstr, i, dur, dots);
+ i=0;
+
+ Rest_req * rq = new Rest_req;
+
+ rq->balltype = dur;
+ rq->dots = dots;
+ rq->print();
+ v->add(rq);
+
+ return v;
+}
+
+void
+set_default_duration(String d)
+{
+ int i=0;
+ parse_duration(d, i, default_duration, default_dots);
+}
+
+
+void
+set_default_pitch(String d)
+{
+ int i=0;
+ bool b;
+ int l,s;
+ parse_pitch(d, i, default_octave, b, l,s);
+}
+
+Request*
+get_request(char c)
+{
+ Request* ret=0;
+ switch (c) {
+ case '[':
+ case ']':
+ ret = new Beam_req;
+ break;
+
+ case ')':
+ case '(':
+ ret = new Slur_req;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ switch (c) {
+ case '(':
+ case '[':
+ ret->span()->spantype = Span_req::START;
+ break;
+ case ')':
+ case ']':
+ ret->span()->spantype = Span_req::STOP;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ return ret;
+}
+
+void
+add_requests(Voice_element *v, svec<Request*> &req)
+{
+ for (int i = 0; i < req.sz(); i++) {
+ v->add(req[i]);
+ }
+ req.set_size(0);
+}