From: Han-Wen Nienhuys Date: Wed, 9 Oct 1996 12:04:47 +0000 (+0200) Subject: release: 0.0.1 X-Git-Tag: release/0.0.1 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=675bd3e6ea001c3af033b51a6e2eeab6a5809e86;p=lilypond.git release: 0.0.1 --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..eae32f81a7 --- /dev/null +++ b/Makefile @@ -0,0 +1,78 @@ +MAJVER=0 +MINVER=0 +PATCHLEVEL=1 + +# +# + +include Sources.make +progdocs=$(hdr) $(mycc) +gencc=parser.cc lexer.cc +cc=$(mycc) $(gencc) +obs=$(cc:.cc=.o) + + +#dist + +DOCDIR=docdir +VERSION=$(MAJVER).$(MINVER).$(PATCHLEVEL) +PACKAGENAME=lilypond +DNAME=$(PACKAGENAME)-$(VERSION) +othersrc=lexer.l parser.y +OFILES=Makefile Sources.make symbol.ini suzan.ly depend lilyponddefs.tex test.tex make_version +DFILES=$(hdr) $(mycc) $(othersrc) $(OFILES) + +#compiling +LOADLIBES=-L$(FLOWERDIR) -lflower +FLOWERDIR=../flower +CXXFLAGS=-I$(FLOWERDIR) -pipe -Wall -g + +exe=$(PACKAGENAME) + + + +$(exe): $(obs) + $(CXX) -o $(exe) $(obs) $(LOADLIBES) +clean: + rm -f $(exe) *.o $(DOCDIR)/* TAGS + +all: kompijl doc + +# doc++ documentation of classes +doc: + -mkdir $(DOCDIR) + doc++ -p -I -d $(DOCDIR) $(progdocs) + +back: + zip -u ~/backs/spacer *cc *hh + +depend: Sources.make + $(CXX) $(CXXFLAGS) -MM $(cc) > depend + +include depend + +parser.cc: parser.y + bison -d $< + mv parser.tab.h parser.hh + mv parser.tab.c parser.cc + +parser.hh: parser.cc + +version.o: $(obs) version.hh + +version.hh: Makefile make_version + make_version $(MAJVER) $(MINVER) $(PATCHLEVEL) > version.hh + +lexer.cc: lexer.l + flex -+ -t lexer.l > lexer.cc + +DDIR=$(DNAME) +dist: + -mkdir $(DDIR) + ln $(DFILES) $(DDIR)/ + tar cfz $(DNAME).tar.gz $(DDIR)/* + rm -rf $(DDIR)/ + + +TAGS: $(mycc) $(hdr) Sources.make + etags -CT $(mycc) $(hdr) diff --git a/Sources.make b/Sources.make new file mode 100644 index 0000000000..8c6744f8e9 --- /dev/null +++ b/Sources.make @@ -0,0 +1,20 @@ +hdr=matrix.hh qlp.hh smat.hh vsmat.hh\ + linespace.hh qlpsolve.hh vector.hh\ + cols.hh proto.hh pstaff.hh line.hh\ + const.hh glob.hh molecule.hh boxes.hh pscore.hh item.hh tex.hh\ + request.hh voice.hh command.hh staff.hh linestaff.hh \ + tstream.hh dstream.hh mtime.hh rhythmstaf.hh\ + parseconstruct.hh real.hh debug.hh globvars.hh keyword.hh\ + misc.hh score.hh notename.hh lexer.hh symtable.hh\ + choleski.hh + +mycc=smat.cc matrix.cc choleski.cc qlp.cc qlpsolve.cc \ + break.cc linespace.cc molecule.cc line.cc\ + pscore.cc tex.cc item.cc cols.cc staff.cc rhythmstaf.cc\ + score.cc note.cc dstream.cc main.cc misc.cc \ + symbol.cc request.cc notename.cc voice.cc\ + keyword.cc linestaff.cc table.cc command.cc\ + warn.cc debug.cc symtable.cc boxes.cc\ + pstaff.cc vector.cc tstream.cc version.cc\ + calcideal.cc\ + template.cc template2.cc diff --git a/boxes.cc b/boxes.cc new file mode 100644 index 0000000000..e042c25333 --- /dev/null +++ b/boxes.cc @@ -0,0 +1,22 @@ +#include "boxes.hh" +#include "const.hh" + +void +Interval:: set_empty() { + min = INFTY; + max = -INFTY; +} + +Box::Box(svec s) +{ + assert(s.sz() == 4); + x.min = s[0]; + x.max = s[1]; + y.min = s[2]; + y.max = s[3]; +} + +Box::Box() +{ + +} diff --git a/boxes.hh b/boxes.hh new file mode 100644 index 0000000000..4413169596 --- /dev/null +++ b/boxes.hh @@ -0,0 +1,82 @@ +/* + some 2D geometrical concepts +*/ + +#ifndef BOXES_HH +#define BOXES_HH + +#include "textdb.hh" +#include "real.hh" +#include "vray.hh" + +/// 2d vector +struct Offset { + Real x,y; + + Offset operator+(Offset o)const { + Offset r(*this); + r+=o; + return r; + } + + Offset operator+=(Offset o) { + x+=o.x; + y+=o.y; + return *this; + } + Offset(Real ix , Real iy) { + x=ix; + y=iy; + } + Offset() { + x=0.0; + y=0.0; + } +}; + +/// a Real interval +struct Interval { + Real min, max; + + void translate(Real t) { + min += t; + max += t; + } + + void unite(Interval h) { + if (h.minmax) + max = h.max; + } + + void set_empty() ; + bool empty() { return min > max; } + Interval() { + set_empty(); + } + Interval(Real m, Real M) { + min =m; + max = M; + } +}; + + +/// a 4-tuple of #Real#s +struct Box { + Interval x, y; + + void translate(Offset o) { + x.translate(o.x); + y.translate(o.y); + } + void unite(Box b) { + x.unite(b.x); + y.unite(b.y); + } + Box(svec ); + Box(); +}; + + +#endif diff --git a/break.cc b/break.cc new file mode 100644 index 0000000000..8f76d13fdc --- /dev/null +++ b/break.cc @@ -0,0 +1,106 @@ +/* + do calculations for breaking problem + + */ + +#include "linespace.hh" +#include "debug.hh" +#include "line.hh" +#include "pscore.hh" + +// construct an appropriate Spacing_problem and solve it. +svec +PScore::solve_line(svec curline) const +{ + Spacing_problem sp; + // mtor << "line of " << curline.sz() << " cols\n"; + 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, linewidth); + + // misschien moeven uit Spacing_problem? + for (PCursor i(suz); i.ok(); i++) { + sp.add_ideal(i); + } + svec the_sol=sp.solve(); + return the_sol; +} + + +void +PScore::problem_OK() +{ + if (!cols.size()) + error("PScore::problem_OK(): Score does not have any columns"); + PCursor start(cols); + PCursor end (cols.bottom()); + + assert(start->breakable); + assert(end->breakable); +} + +struct Col_configuration { + svec line; + svec config; + Real energy; + + Col_configuration() { + energy = INFTY; + } + void add(const PCol*c) { line.add(c);} + void setsol(svec sol) { + config = sol; + energy = config.last(); + config.pop(); + } +}; + +/// wordwrap type algorithm +/* el stupido. This should be optimised... + */ + +void +PScore::calc_breaking() +{ + problem_OK(); + PCursor curcol(cols); + + svec breakpoints(find_breaks()); + assert(breakpoints.sz()>=2); + for (int i=0 ; i < breakpoints.sz() -1; ) { + Col_configuration minimum; + Col_configuration current; + + // do another line + current.add(breakpoints[i]->postbreak ); + 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 ); + current.setsol(solve_line(current.line)); + + + if (current.energy < minimum.energy) { + minimum = current; + } else { + break; + } + + current.line.last()=breakpoints[i]; + curcol ++; + i++; + } + + add_line(minimum.line, minimum.config); + } +} + diff --git a/calcideal.cc b/calcideal.cc new file mode 100644 index 0000000000..b1410aefc0 --- /dev/null +++ b/calcideal.cc @@ -0,0 +1,64 @@ +#include "tstream.hh" +#include "score.hh" +#include "pscore.hh" +#include "staff.hh" +#include "misc.hh" +#include "debug.hh" + + +void +Score::do_connect(PCol *c1, PCol *c2, Real d) +{ + Idealspacing*sp=pscore_->get_spacing(c1,c2); + + if (!sp->hooke){ + sp->hooke = 1.0; + sp->space =d; + } +} + +void +Score::connect_nonmus(PCol* c1, PCol *c2, Real d) +{ + if (c2->used && c1->used) { + do_connect(c1,c2,d); + + // alert! this is broken! + if (c1->breakable) { + do_connect(c1->postbreak, c2,d); + } + if (c2->breakable) { + do_connect(c1, c2->prebreak,d); + } + if (c1->breakable &&c2->breakable) { + do_connect(c1->postbreak, c2->prebreak,d); + } + } +} +/* this needs A LOT of rethinking. + + generate springs between columns. + */ +void +Score::calc_idealspacing() +{ + PCursor sc(cols_); + + for (; sc.ok(); sc++) { + if (sc->musical) + for (int i=0; i < sc->durations.sz(); i++) { + Mtime d = sc->durations[i]; + Real dist = duration_to_idealspace(d); + PCol * c2 = find_col(sc->when + d,true)->pcol; + connect_nonmus(sc->pcol, c2, dist); + c2 = find_col(sc->when + d,false)->pcol; + connect_nonmus(sc->pcol, c2, dist); + } + else if (sc->used()) { // ignore empty columns + PCol * c2 = find_col(sc->when,true)->pcol; + connect_nonmus(sc->pcol,c2,0.0); + } + } +} + + diff --git a/choleski.cc b/choleski.cc new file mode 100644 index 0000000000..db3fe82d60 --- /dev/null +++ b/choleski.cc @@ -0,0 +1,85 @@ +#include "choleski.hh" + +Vector +Choleski_decomposition::solve(Vector rhs)const +{ + int n= rhs.dim(); + assert(n == L.dim()); + Vector y(n); + + // forward substitution + for (int i=0; i < n; i++) { + Real sum(0.0); + for (int j=0; j < i; j++) + sum += y(j) * L(i,j); + y(i) = (rhs(i) - sum)/L(i,i); + } + for (int i=0; i < n; i++) { + assert(D(i)); + y(i) /= D(i); + } + Vector x(n); + for (int i=n-1; i >= 0; i--) { + Real sum(0.0); + for (int j=i+1; j < n; j++) + sum += L(j,i)*x(j); + x(i) = (y(i) - sum)/L(i,i); + } + return x; +} + +Choleski_decomposition::Choleski_decomposition(Matrix P) + : L(P.dim()), D(P.dim()) +{ + int n = P.dim(); + assert((P-P.transposed()).norm() < EPS); + L.unit(); + for (int k= 0; k < n; k++) { + for (int j = 0; j < k; j++){ + Real sum(0.0); + for (int l=0; l < j; l++) + sum += L(k,l)*L(j,l)*D(l); + L(k,j) = (P(k,j) - sum)/D(j); + } + Real sum=0.0; + + for (int l=0; l < k; l++) + sum += sqr(L(k,l))*D(l); + Real d = P(k,k) - sum; + D(k) = d; + } + + #ifdef NDEBUG + assert((original()-P).norm() < EPS); + #endif +} + +Matrix +Choleski_decomposition::original() const +{ + Matrix T(L.dim()); + T.set_diag(D); + return L*T*L.transposed(); +} + +Matrix +Choleski_decomposition::inverse() const +{ + int n=L.dim(); + Matrix invm(n); + Vector e_i(n); + for (int i = 0; i < n; i++) { + e_i.set_unit(i); + Vector inv(solve(e_i)); + for (int j = 0 ; j= 0 && left && right); +} + +Interval +PCol::width() const +{ + Interval w; + + for (PCursor ic(its); ic.ok(); ic++) + w.unite(ic->width()); + if (w.empty()) + w.unite(Interval(0,0)); + return w; +} +/****************************************************************/ + +int +PCol::compare(const PCol &c1, const PCol &c2) +{ + assert(false); +} + +void +PCol::OK () const +{ + if (prebreak || postbreak ) { + assert(breakable); + } +} + +void +PCol::set_breakable() +{ + if (breakable) + return; + + prebreak = new PCol(this); + postbreak = new PCol(this); + breakable = true; + used = true; +} + +PCol::PCol(PCol *parent) { + daddy = parent; + prebreak=0; + postbreak=0; + breakable=false; + line=0; + used = false; +} + +PCol::~PCol() +{ + delete prebreak; + delete postbreak; +} + +void +PCol::add(const Item *i) +{ + its.bottom().add(i); + used = true; +} + diff --git a/cols.hh b/cols.hh new file mode 100644 index 0000000000..e51e2d56c0 --- /dev/null +++ b/cols.hh @@ -0,0 +1,96 @@ +#ifndef COLS_HH +#define COLS_HH + +#include "glob.hh" +#include "boxes.hh" +#include "list.hh" +#include "item.hh" + +/// stuff grouped vertically. +struct PCol { + List its; + List stoppers, starters; + + /// Can this be broken? true eg. for bars. + bool breakable; + + /// does this column have items, does it have spacings attached? + bool used; + + /// prebreak is put before end of line. + PCol *prebreak; + /** + if broken here, then (*this) column is discarded, and prebreak + is put at end of line, owned by Col + */ + + /// postbreak at beginning of the new line + PCol *postbreak; + /** \See{prebreak} + */ + PCol *daddy; + /* + if this column is pre or postbreak, then this field points to the parent. + */ + /// if lines are broken then this column is in #line# + const Line_of_score *line; + + /// if lines are broken then this column x-coord #hpos# + Real hpos; + + + /****************************************************************/ + + void add(const Item*i); + + Interval width() const; + ~PCol(); + PCol(PCol * parent); + /// initialize the prebreak and postbreak fields + setup_breaks(); + + /// which col comes first? + static int compare(const PCol &c1, const PCol &c2); + /** + signed compare on columns. + + return < 0 if c1 < c2. + */ + + void OK() const; + void set_breakable(); + +}; +/** + This is a class to address items vertically. It contains the data for: + \begin{itemize} + \item + unbroken score + \item + broken score + \item + the linespacing problem + \end{itemize} + */ + +#include "compare.hh" +instantiate_compare(const PCol &, PCol::compare); + + +/// ideal spacing between two columns +struct Idealspacing { + + /// the ideal distance + Real space; + + /// Hooke's constant: how strong are the "springs" attached to columns + Real hooke; + + /// the two columns + const PCol *left, *right; + + void OK() const ; + Idealspacing(const PCol *left,const PCol *right); +}; + +#endif diff --git a/command.cc b/command.cc new file mode 100644 index 0000000000..bb86e4fd34 --- /dev/null +++ b/command.cc @@ -0,0 +1,13 @@ +#include "string.hh" +#include "command.hh" + + +Command* +get_bar_command(Real w) +{ + Command*c = new Command; + c->when = w; + c->code = TYPESET; + c->args.add( "|"); + return c; +} diff --git a/command.hh b/command.hh new file mode 100644 index 0000000000..353bf5d4bb --- /dev/null +++ b/command.hh @@ -0,0 +1,69 @@ +#ifndef COMMAND_HH +#define COMMAND_HH +#include "glob.hh" +#include "mtime.hh" +#include "vray.hh" +enum Commandcode { + NOP, + INTERPRET, + TYPESET, + BREAK_PRE,BREAK_MIDDLE, BREAK_POST, BREAK_END, +}; +/// set a nonrythmical symbol +struct Command { + Commandcode code; + + Mtime when; + /// analogous to argv[] + svec args; +}; + +/** + A nonrhythmical "thing" in a staff is called a "command". + Commands have these properties: + + \begin{itemize} + \item They are \bf{not} rhythmical, i.e. they do not have a duration + \item They have a staff-wide impact, i.e. a command cannot be targeted at + only one voice in the staff: two voices sharing a staff can't have + different clefs + \item Commands are ordered, that is, when from musical point of view the + commands happen simultaneously, the order in which Staff receives the + commands can still make a difference in the output + \item Some commands are actually score wide, so Score has to issue these + commands to the Staff, eg. BREAK commands + \end{itemize} + + At this moment we have three classes of commands: + \begin{description} + INTERPRET commands are not grouped. + \item[TYPESET] These commands instruct the Staff to + typeset symbols on the output, eg meter/clef/key changes + \item[INTERPRET] These commands do not produce output, instead, + they change the interpretation of other commands or requests. + example: shift output vertically, set the key. + \item[BREAK_XXX] These commands group TYPESET commands in + prebreak and postbreak commands. \See{Col}. + Staff can insert additional commands in a sequence of BREAK_XXX + commands, eg. key change commands + + \end{description} + + These commands are generated by Score, since they have to be the + same for the whole score. + + + \begin{description} + \item[BREAK_PRE] + \item[BREAK_MIDDLE] + \item[BREAK_POST] + \item[BREAK_END] + \item[TYPESET] METER,BAR + \end{description} + + + Commands can be freely copied, they do not have virtual methods. + */ + + +#endif diff --git a/const.hh b/const.hh new file mode 100644 index 0000000000..5a989e2e4c --- /dev/null +++ b/const.hh @@ -0,0 +1,11 @@ +/* + global constants + */ + + +const Real CM_TO_PT=72/2.54; +const Real VERT_TO_PT=CM_TO_PT; // tex output +const Real HOR_TO_PT=CM_TO_PT; // tex output +const Real EPS=1e-7; // qlpsolve.hh +const int MAXITER=100; // qlpsolve.hh +const Real INFTY=1e8; diff --git a/debug.cc b/debug.cc new file mode 100644 index 0000000000..548a76c186 --- /dev/null +++ b/debug.cc @@ -0,0 +1,15 @@ +#include "debug.hh" +int debug_flags; + +void +set_debug(String s) +{ + if (s.pos ('t')) { + debug_flags |= DEBUGTOKEN; + mtor << " Turning on token debug\n"; + } + if (s.pos ('p')){ + debug_flags |= DEBUGPARSER; + mtor << "Turning on parser debugger\n"; + } +} diff --git a/debug.hh b/debug.hh new file mode 100644 index 0000000000..fce60a9816 --- /dev/null +++ b/debug.hh @@ -0,0 +1,25 @@ +#ifndef DEBUG_HH +#define DEBUG_HH +#include +#include +#include "dstream.hh" + + + +#define WARN warnout << "warning: "<<__FUNCTION__ << ": " +extern ostream &warnout ; +extern ostream *mlog; +extern dstream mtor; // monitor + +void error(String s); +void warning(String s); + +extern int debug_flags; + +void +set_debug(String s); +const int DEBUGPARSER = 0x01; +const int DEBUGTOKEN = 0x02; +const int DEBUGITEMS = 0x04; + +#endif diff --git a/depend b/depend new file mode 100644 index 0000000000..fdb5acec60 --- /dev/null +++ b/depend @@ -0,0 +1,179 @@ +smat.o: smat.cc smat.hh ../flower/vray.hh vsmat.hh real.hh +matrix.o: matrix.cc matrix.hh vsmat.hh ../flower/vray.hh real.hh \ + vector.hh glob.hh proto.hh const.hh ../flower/string.hh \ + ../flower/stringutil.hh debug.hh dstream.hh +choleski.o: choleski.cc choleski.hh matrix.hh vsmat.hh \ + ../flower/vray.hh real.hh vector.hh glob.hh proto.hh const.hh +qlp.o: qlp.cc debug.hh dstream.hh ../flower/string.hh \ + ../flower/stringutil.hh qlp.hh matrix.hh vsmat.hh ../flower/vray.hh \ + real.hh vector.hh glob.hh proto.hh const.hh choleski.hh +qlpsolve.o: qlpsolve.cc qlpsolve.hh qlp.hh matrix.hh vsmat.hh \ + ../flower/vray.hh real.hh vector.hh glob.hh proto.hh const.hh \ + debug.hh dstream.hh ../flower/string.hh ../flower/stringutil.hh \ + choleski.hh +break.o: break.cc linespace.hh glob.hh real.hh proto.hh const.hh \ + ../flower/list.hh ../flower/list.inl ../flower/cursor.hh \ + ../flower/link.hh ../flower/link.inl ../flower/cursor.inl \ + ../flower/vray.hh cols.hh boxes.hh ../flower/textdb.hh \ + ../flower/textstr.hh ../flower/string.hh ../flower/stringutil.hh \ + item.hh tex.hh ../flower/compare.hh matrix.hh vsmat.hh vector.hh \ + debug.hh dstream.hh line.hh pstaff.hh pscore.hh +linespace.o: linespace.cc linespace.hh glob.hh real.hh proto.hh \ + const.hh ../flower/list.hh ../flower/list.inl ../flower/cursor.hh \ + ../flower/link.hh ../flower/link.inl ../flower/cursor.inl \ + ../flower/vray.hh cols.hh boxes.hh ../flower/textdb.hh \ + ../flower/textstr.hh ../flower/string.hh ../flower/stringutil.hh \ + item.hh tex.hh ../flower/compare.hh matrix.hh vsmat.hh vector.hh \ + debug.hh dstream.hh qlp.hh ../flower/unionfind.hh +molecule.o: molecule.cc glob.hh real.hh proto.hh const.hh \ + ../flower/string.hh ../flower/stringutil.hh molecule.hh \ + ../flower/list.hh ../flower/list.inl ../flower/cursor.hh \ + ../flower/link.hh ../flower/link.inl ../flower/cursor.inl boxes.hh \ + ../flower/textdb.hh ../flower/textstr.hh ../flower/vray.hh item.hh \ + tex.hh +line.o: line.cc line.hh real.hh ../flower/list.hh ../flower/list.inl \ + ../flower/cursor.hh ../flower/link.hh ../flower/link.inl \ + ../flower/cursor.inl ../flower/vray.hh glob.hh proto.hh const.hh \ + pstaff.hh item.hh boxes.hh ../flower/textdb.hh ../flower/textstr.hh \ + ../flower/string.hh ../flower/stringutil.hh tex.hh cols.hh \ + ../flower/compare.hh pscore.hh +pscore.o: pscore.cc debug.hh dstream.hh ../flower/string.hh \ + ../flower/stringutil.hh line.hh real.hh ../flower/list.hh \ + ../flower/list.inl ../flower/cursor.hh ../flower/link.hh \ + ../flower/link.inl ../flower/cursor.inl ../flower/vray.hh glob.hh \ + proto.hh const.hh pstaff.hh item.hh boxes.hh ../flower/textdb.hh \ + ../flower/textstr.hh tex.hh pscore.hh cols.hh ../flower/compare.hh \ + tstream.hh +tex.o: tex.cc tex.hh ../flower/string.hh ../flower/stringutil.hh \ + boxes.hh ../flower/textdb.hh ../flower/textstr.hh ../flower/vray.hh \ + real.hh const.hh +item.o: item.cc line.hh real.hh ../flower/list.hh ../flower/list.inl \ + ../flower/cursor.hh ../flower/link.hh ../flower/link.inl \ + ../flower/cursor.inl ../flower/vray.hh glob.hh proto.hh const.hh \ + pstaff.hh item.hh boxes.hh ../flower/textdb.hh ../flower/textstr.hh \ + ../flower/string.hh ../flower/stringutil.hh tex.hh cols.hh \ + ../flower/compare.hh +cols.o: cols.cc cols.hh glob.hh real.hh proto.hh const.hh boxes.hh \ + ../flower/textdb.hh ../flower/textstr.hh ../flower/string.hh \ + ../flower/stringutil.hh ../flower/vray.hh ../flower/list.hh \ + ../flower/list.inl ../flower/cursor.hh ../flower/link.hh \ + ../flower/link.inl ../flower/cursor.inl item.hh tex.hh \ + ../flower/compare.hh +staff.o: staff.cc staff.hh score.hh ../flower/vray.hh cols.hh glob.hh \ + real.hh proto.hh const.hh boxes.hh ../flower/textdb.hh \ + ../flower/textstr.hh ../flower/string.hh ../flower/stringutil.hh \ + ../flower/list.hh ../flower/list.inl ../flower/cursor.hh \ + ../flower/link.hh ../flower/link.inl ../flower/cursor.inl item.hh \ + tex.hh ../flower/compare.hh mtime.hh command.hh voice.hh request.hh \ + debug.hh dstream.hh pscore.hh pstaff.hh +rhythmstaf.o: rhythmstaf.cc request.hh glob.hh real.hh proto.hh \ + const.hh ../flower/string.hh ../flower/stringutil.hh mtime.hh \ + debug.hh dstream.hh linestaff.hh pstaff.hh ../flower/list.hh \ + ../flower/list.inl ../flower/cursor.hh ../flower/link.hh \ + ../flower/link.inl ../flower/cursor.inl item.hh boxes.hh \ + ../flower/textdb.hh ../flower/textstr.hh ../flower/vray.hh tex.hh \ + staff.hh score.hh cols.hh ../flower/compare.hh command.hh voice.hh \ + pscore.hh molecule.hh rhythmstaf.hh +score.o: score.cc tstream.hh ../flower/string.hh \ + ../flower/stringutil.hh score.hh ../flower/vray.hh cols.hh glob.hh \ + real.hh proto.hh const.hh boxes.hh ../flower/textdb.hh \ + ../flower/textstr.hh ../flower/list.hh ../flower/list.inl \ + ../flower/cursor.hh ../flower/link.hh ../flower/link.inl \ + ../flower/cursor.inl item.hh tex.hh ../flower/compare.hh mtime.hh \ + command.hh pscore.hh pstaff.hh staff.hh voice.hh request.hh misc.hh \ + debug.hh dstream.hh +note.o: note.cc ../flower/string.hh ../flower/stringutil.hh real.hh \ + debug.hh dstream.hh request.hh glob.hh proto.hh const.hh mtime.hh \ + voice.hh ../flower/list.hh ../flower/list.inl ../flower/cursor.hh \ + ../flower/link.hh ../flower/link.inl ../flower/cursor.inl notename.hh +dstream.o: dstream.cc dstream.hh ../flower/string.hh \ + ../flower/stringutil.hh +main.o: main.cc ../flower/lgetopt.hh misc.hh mtime.hh real.hh debug.hh \ + dstream.hh ../flower/string.hh ../flower/stringutil.hh score.hh \ + ../flower/vray.hh cols.hh glob.hh proto.hh const.hh boxes.hh \ + ../flower/textdb.hh ../flower/textstr.hh ../flower/list.hh \ + ../flower/list.inl ../flower/cursor.hh ../flower/link.hh \ + ../flower/link.inl ../flower/cursor.inl item.hh tex.hh \ + ../flower/compare.hh command.hh globvars.hh +misc.o: misc.cc misc.hh mtime.hh real.hh glob.hh proto.hh const.hh +symbol.o: symbol.cc tex.hh ../flower/string.hh ../flower/stringutil.hh \ + boxes.hh ../flower/textdb.hh ../flower/textstr.hh ../flower/vray.hh \ + real.hh +request.o: request.cc request.hh glob.hh real.hh proto.hh const.hh \ + ../flower/string.hh ../flower/stringutil.hh mtime.hh +notename.o: notename.cc glob.hh real.hh proto.hh const.hh \ + ../flower/string.hh ../flower/stringutil.hh +voice.o: voice.cc debug.hh dstream.hh ../flower/string.hh \ + ../flower/stringutil.hh voice.hh mtime.hh real.hh ../flower/list.hh \ + ../flower/list.inl ../flower/cursor.hh ../flower/link.hh \ + ../flower/link.inl ../flower/cursor.inl request.hh glob.hh proto.hh \ + const.hh +keyword.o: keyword.cc glob.hh real.hh proto.hh const.hh lexer.hh +linestaff.o: linestaff.cc linestaff.hh pstaff.hh ../flower/list.hh \ + ../flower/list.inl ../flower/cursor.hh ../flower/link.hh \ + ../flower/link.inl ../flower/cursor.inl item.hh glob.hh real.hh \ + proto.hh const.hh boxes.hh ../flower/textdb.hh ../flower/textstr.hh \ + ../flower/string.hh ../flower/stringutil.hh ../flower/vray.hh tex.hh +table.o: table.cc glob.hh real.hh proto.hh const.hh \ + ../flower/string.hh ../flower/stringutil.hh keyword.hh parser.hh +command.o: command.cc ../flower/string.hh ../flower/stringutil.hh \ + command.hh glob.hh real.hh proto.hh const.hh mtime.hh \ + ../flower/vray.hh +warn.o: warn.cc debug.hh dstream.hh ../flower/string.hh \ + ../flower/stringutil.hh +debug.o: debug.cc debug.hh dstream.hh ../flower/string.hh \ + ../flower/stringutil.hh +symtable.o: symtable.cc misc.hh mtime.hh real.hh debug.hh dstream.hh \ + ../flower/string.hh ../flower/stringutil.hh tex.hh boxes.hh \ + ../flower/textdb.hh ../flower/textstr.hh ../flower/vray.hh \ + ../flower/assoc.hh symtable.hh const.hh +boxes.o: boxes.cc boxes.hh ../flower/textdb.hh ../flower/textstr.hh \ + ../flower/string.hh ../flower/stringutil.hh ../flower/vray.hh real.hh \ + const.hh +pstaff.o: pstaff.cc pstaff.hh ../flower/list.hh ../flower/list.inl \ + ../flower/cursor.hh ../flower/link.hh ../flower/link.inl \ + ../flower/cursor.inl item.hh glob.hh real.hh proto.hh const.hh \ + boxes.hh ../flower/textdb.hh ../flower/textstr.hh ../flower/string.hh \ + ../flower/stringutil.hh ../flower/vray.hh tex.hh +vector.o: vector.cc debug.hh dstream.hh ../flower/string.hh \ + ../flower/stringutil.hh vector.hh glob.hh real.hh proto.hh const.hh \ + ../flower/vray.hh +tstream.o: tstream.cc tex.hh ../flower/string.hh \ + ../flower/stringutil.hh boxes.hh ../flower/textdb.hh \ + ../flower/textstr.hh ../flower/vray.hh real.hh tstream.hh debug.hh \ + dstream.hh +version.o: version.cc ../flower/string.hh ../flower/stringutil.hh +calcideal.o: calcideal.cc tstream.hh ../flower/string.hh \ + ../flower/stringutil.hh score.hh ../flower/vray.hh cols.hh glob.hh \ + real.hh proto.hh const.hh boxes.hh ../flower/textdb.hh \ + ../flower/textstr.hh ../flower/list.hh ../flower/list.inl \ + ../flower/cursor.hh ../flower/link.hh ../flower/link.inl \ + ../flower/cursor.inl item.hh tex.hh ../flower/compare.hh mtime.hh \ + command.hh pscore.hh pstaff.hh staff.hh voice.hh request.hh misc.hh \ + debug.hh dstream.hh +template.o: template.cc line.hh real.hh ../flower/list.hh \ + ../flower/list.inl ../flower/cursor.hh ../flower/link.hh \ + ../flower/link.inl ../flower/cursor.inl ../flower/vray.hh glob.hh \ + proto.hh const.hh pstaff.hh item.hh boxes.hh ../flower/textdb.hh \ + ../flower/textstr.hh ../flower/string.hh ../flower/stringutil.hh \ + tex.hh cols.hh ../flower/compare.hh request.hh mtime.hh score.hh \ + command.hh staff.hh voice.hh ../flower/list.cc ../flower/cursor.cc +template2.o: template2.cc line.hh real.hh ../flower/list.hh \ + ../flower/list.inl ../flower/cursor.hh ../flower/link.hh \ + ../flower/link.inl ../flower/cursor.inl ../flower/vray.hh glob.hh \ + proto.hh const.hh pstaff.hh item.hh boxes.hh ../flower/textdb.hh \ + ../flower/textstr.hh ../flower/string.hh ../flower/stringutil.hh \ + tex.hh voice.hh mtime.hh request.hh molecule.hh staff.hh score.hh \ + cols.hh ../flower/compare.hh command.hh ../flower/list.cc \ + ../flower/cursor.cc +parser.o: parser.cc lexer.hh proto.hh real.hh staff.hh score.hh \ + ../flower/vray.hh cols.hh glob.hh const.hh boxes.hh \ + ../flower/textdb.hh ../flower/textstr.hh ../flower/string.hh \ + ../flower/stringutil.hh ../flower/list.hh ../flower/list.inl \ + ../flower/cursor.hh ../flower/link.hh ../flower/link.inl \ + ../flower/cursor.inl item.hh tex.hh ../flower/compare.hh mtime.hh \ + command.hh voice.hh request.hh keyword.hh globvars.hh debug.hh \ + dstream.hh parseconstruct.hh +lexer.o: lexer.cc glob.hh real.hh proto.hh const.hh \ + ../flower/string.hh ../flower/stringutil.hh lexer.hh keyword.hh \ + ../flower/vray.hh parser.hh debug.hh dstream.hh diff --git a/dstream.cc b/dstream.cc new file mode 100644 index 0000000000..197e5300cf --- /dev/null +++ b/dstream.cc @@ -0,0 +1,46 @@ +// implementation of debug/TeX stream. +#include + +#include "dstream.hh" +#include "string.hh" + + + +dstream mtor(cout); + +/// +dstream & +dstream::operator<<(String s) +{ + for (const char *cp = s ; *cp; cp++) + switch(*cp) + { + case '{': + case '[': + case '(': indentlvl += INDTAB; + *os << *cp; + break; + + case ')': + case ']': + case '}': + indentlvl -= INDTAB; + *os << *cp ; + + if (indentlvl<0) indentlvl = 0; + break; + + case '\n': + *os << '\n' << String (' ', indentlvl) << flush; + break; + default: + *os << *cp; + break; + } + return *this; + +} + +/** only output possibility. Delegates all conversion to String class. + */ + diff --git a/dstream.hh b/dstream.hh new file mode 100644 index 0000000000..3a47a84784 --- /dev/null +++ b/dstream.hh @@ -0,0 +1,29 @@ +// debug_stream + +#ifndef DSTREAM_HH +#define DSTREAM_HH + +#include "string.hh" + +const char eol= '\n'; + +/// debug stream +class dstream +{ + ostream *os; + int indentlvl; + + /// indent of each level + const INDTAB = 3; +public: + dstream(ostream &r){ + os = &r; + indentlvl = 0; + } + dstream &operator << (String s); +}; + /** + a class for providing debug output of nested structures, + with indents according to \{\}()[] + */ +#endif diff --git a/flower/Makefile b/flower/Makefile new file mode 100644 index 0000000000..5fb4f2a5b6 --- /dev/null +++ b/flower/Makefile @@ -0,0 +1,43 @@ +MAJVER=1 +MINVER=0 +PATCHLEVEL=1 + +PACKAGENAME=flower +VERSION=$(MAJVER).$(MINVER).$(PATCHLEVEL) +DNAME=$(PACKAGENAME)-$(VERSION) +CXXFLAGS+=-g -Wall + +cc=lgetopt.cc string.cc dataf.cc textdb.cc unionfind.cc + +templatecc=cursor.cc list.cc +inl=findcurs.inl link.inl list.inl +hh=cursor.hh cursor.inl lgetopt.hh link.hh list.hh \ + string.hh stringutil.hh vray.hh textdb.hh textstr.hh assoc.hh\ + findcurs.hh unionfind.hh compare.hh handle.hh + + + +obs=$(cc:.cc=.o) +staticlib=libflower.a +$(staticlib): $(obs) + ar cr libflower.a $(obs) + +include depend + +depend: Makefile + $(CXX) -MM $(cc) > depend + + +clean: + rm depend $(obs) $(staticlib) + +DFILES=$(hh) $(cc) $(inl) $(templatecc) Makefile +DDIR=$(DNAME) + +dist: + -mkdir $(DDIR) + ln $(DFILES) $(DDIR)/ + tar cfz $(DNAME).tar.gz $(DDIR)/* + rm -rf $(DDIR)/ + + diff --git a/flower/assoc.hh b/flower/assoc.hh new file mode 100644 index 0000000000..a2173026f4 --- /dev/null +++ b/flower/assoc.hh @@ -0,0 +1,73 @@ +#ifndef ASSOC_HH +#define ASSOC_HH + +#include "vray.hh" + +template +struct Assoc_ent_ { + bool free; + K key; + V val; +}; + +template +struct Assoc { + svec< Assoc_ent_ > arr; + + /****************/ + + int find(K key) const { + for (int i = 0; i < arr.sz(); i++) { + if (!arr[i].free && key == arr[i].key) + return i; + } + return -1; + } + int find_creat(K key) { + int free = -1; + for (int i = 0; i < arr.sz(); i++) { + if (key == arr[i].key) { + return i; + } else if (arr[i].free ) { + free = i; + } + } + if (free >= 0){ + arr[free].free = false; + arr[free].key = key; + return free; + } + + Assoc_ent_ ae; + ae.free = false; + ae.key = key; + arr.add(ae); + return arr.sz() -1; + } +public: + bool elt_query(K key) const { + return find(key) >= 0; + } + void del(K key) { + assert(elt_query(key)); + int i= find(key); + arr[i].free = true; + } + void + add(K key, V val) { + int i = find_creat(key); + arr[i].val = val; + } + /** + should create "set" template + */ + V& operator[](K key) { + return arr[find_creat(key)].val; + } + const V& operator[](K key) const { + assert(elt_query(key)); + return arr[find(key)].val; + } + +}; +#endif diff --git a/flower/compare.hh b/flower/compare.hh new file mode 100644 index 0000000000..05b6e89e41 --- /dev/null +++ b/flower/compare.hh @@ -0,0 +1,24 @@ +#ifndef COMPARE_HH +#define COMPARE_HH + +/// handy notations for a signed comparison +#define instantiate_compare(type, function) \ +inline bool operator>(type t1, type t2) { return function(t1, t2) > 0; } \ + inline bool operator>=(type t1, type t2) { return function(t1, t2) >= 0; } \ + inline bool operator==(type t1, type t2) { return function(t1, t2) == 0; } \ + inline bool operator<=(type t1, type t2) { return function(t1, t2) <= 0; } \ + inline bool operator<(type t1, type t2) { return function(t1, t2) < 0; } \ + inline type MAX(type t1, type t2) { return (t1 > t2 )? t1 : t2; }\ + inline type MIN(type t1, type t2) { return (t1 < t2 )? t1 : t2; }\ + \ + bool operator<(type t1, type t2) /* stupid fix to allow ; */ + /** + make the operators{<,<=,==,>=,>} and the MAX and MIN of two. + Please fill a & in the type argument if necessary. + */ + + + + +#endif + diff --git a/flower/cursor.cc b/flower/cursor.cc new file mode 100644 index 0000000000..10e90de919 --- /dev/null +++ b/flower/cursor.cc @@ -0,0 +1,75 @@ +// cursor.cc +#ifndef CURSOR_CC +#define CURSOR_CC + +#include "cursor.hh" +//#define inline +//#include "cursor.inl" +#include + +template +Cursor +Cursor::operator ++( int ) +{ + Cursor r = *this; + assert( pointer_ ); + pointer_ = pointer_->next(); + return r; +} +template +Cursor +Cursor::operator -=( int j ) +{ + while (j--) + (*this)--; + return *this; +} +template +Cursor +Cursor::operator +=( int j ) +{ + while (j++) + (*this)++; + return *this; +} + +template +Cursor +Cursor::operator --( int ) +{ + Cursor r = *this; + assert( pointer_ ); + pointer_ = pointer_->previous(); + return r; +} + +template +Cursor +Cursor::operator +( int i ) const +{ + Cursor r = *this; + + if (i<0) + return r -(-i); + + while (i--) + r++; + + return r; +} + +template +Cursor +Cursor::operator -( int i ) const +{ + Cursor r = *this; + if (i<0) + return r +(-i); + + while (i--) + r--; + + return r; +} + +#endif diff --git a/flower/cursor.hh b/flower/cursor.hh new file mode 100644 index 0000000000..b053591dd4 --- /dev/null +++ b/flower/cursor.hh @@ -0,0 +1,125 @@ +// cursor.hh + +#ifndef __CURSOR_HH +#define __CURSOR_HH + +#include "link.hh" +template class List; + +/// +template +class Cursor +{ + public: + Cursor( List& list, Link* pointer = 0 ); + Cursor( const Cursor& cursor ); + + /// return current T + T& operator *(); + operator T() { return *(*this); } + Cursor operator =( const Cursor& c ); + + /// make cursor with #no# items back + Cursor operator -( int no) const; + + /// make cursor with #no# items further + Cursor operator +( int no) const; + + Cursor operator -=(int); + Cursor operator +=(int); + + /// return current and move one down + Cursor operator ++( int ); + + /// return current and move one up + Cursor operator --( int ); + + /// point to link? + bool ok(); + + /// ++ items left? + bool forward(); + + /// -- items left? + bool backward(); + + /// put (copy) after me in List + void add( const T& thing ); + /** + analogously to editor. ok() interpreted as at end + of line. + + PRE: !ok, POST: added to bottom() + + PRE: ok, POST: added after me + + cursor points to same object, cursor.next() is newly added + object. + */ + + /// put (copy) before me in List + void insert( const T& thing ); + /** + analogously to editor. ok() interpreted as at begin of + line. + + PRE: !ok, POST: add to top() + + PRE: ok, POST: add before me + + cursor points to same object, cursor.previous() + is newly inserted object. + */ + /// remove and cleanup Link // HWN: backspace or del? + void remove(); + + /// access the list this came from + const List& list() const ; + Link* pointer(); + +private: + List& list_; + Link* pointer_; +}; + + +/** + add and insert extend the list + items are always stored as copies in List, but: + List : copies of String stored + List : copies of String* stored! + + the operations add and insert actually delegate the work to List class. + */ + + + +/// cursor which feels like a pointer +template +struct PCursor : public Cursor { + + /// make cursor with #no# items back + PCursor operator -( int no) const { + return PCursor (Cursor::operator-(no)); + } + + /// make cursor with #no# items further + PCursor operator +( int no) const { + return PCursor (Cursor::operator+(no)); + } + PCursor(List & l) : Cursor (l) {} + + PCursor( const Cursor& cursor ) : Cursor(cursor) { } + T operator ->() { return *(*this); } + +}; +/** + HWN: I'd like an operator->(), so here it is. + + Cursor to go with pointer list. + */ + +#include "list.hh" +#include "cursor.inl" + +#endif // __CURSOR_HH // diff --git a/flower/cursor.inl b/flower/cursor.inl new file mode 100644 index 0000000000..0243d14a2b --- /dev/null +++ b/flower/cursor.inl @@ -0,0 +1,100 @@ + // cursor.inl +#ifndef CURSOR_INL +#define CURSOR_INL +#include +//#include "list.hh" + +template +inline +Cursor::Cursor( List& list, Link* pointer ) : + list_( list ) +{ + if ( list.size() ) + pointer_ = pointer ? pointer : list.top().pointer_; + else + pointer_ = pointer; +} + +template +inline +Cursor::Cursor( const Cursor& cursor ) : + list_( cursor.list_ ) +{ + pointer_ = cursor.pointer_; +} + +template +inline T& +Cursor::operator *() +{ + assert( pointer_ ); + return pointer_->thing(); +} + +template +Cursor +Cursor::operator =( const Cursor& c ) +{ + assert( &list_ == &c.list_ ); + pointer_ = c.pointer_; + return *this; +} + +template +inline void +Cursor::add( const T& thing ) +{ + list_.add( thing, *this ); +} + +template +inline void +Cursor::insert( const T& thing ) +{ + list_.insert( thing, *this ); +} + +template +inline void +Cursor::remove() +{ + assert( pointer_ ); + list_.remove( *this ); +} + +template +inline const List& +Cursor::list() const +{ + return list_; +} + +template +inline Link* +Cursor::pointer() +{ + return pointer_; +} + +template +inline bool +Cursor::backward() +{ + return ( pointer_ != 0 ); +} + +template +inline bool +Cursor::forward() +{ + return ( pointer_ != 0 ); +} + +template +inline bool +Cursor::ok() +{ + return ( pointer_ != 0 ); +} + +#endif \ No newline at end of file diff --git a/flower/dataf.cc b/flower/dataf.cc new file mode 100644 index 0000000000..e53c716d8b --- /dev/null +++ b/flower/dataf.cc @@ -0,0 +1,125 @@ +#include +#include + +#include "textstr.hh" +Text_stream::Text_stream(String fn) +{ + if (fn == "") + { + name = ""; + f = stdin; + } + + else + { + name = fn; + f = fopen(fn, "r"); + } + + if (!f) { + cerr <<__FUNCTION__<< ": can't open `" << fn << "'\n"; + exit(1); + } + + line_no = 1; + } + +void +Text_stream::message(String s) +{ + cerr << "\n"< +class FindCursor : public Cursor +{ +public: + FindCursor( List& list, Link* pointer = 0 ); + FindCursor( const Cursor& cursor ); + + Cursor operator =( const Cursor& c ); + FindCursor find( const T& thing ); +}; + +#include "findcurs.inl" diff --git a/flower/findcurs.inl b/flower/findcurs.inl new file mode 100644 index 0000000000..593d4aae10 --- /dev/null +++ b/flower/findcurs.inl @@ -0,0 +1,37 @@ +template +inline +FindCursor::FindCursor( List& list, Link* pointer ) : + Cursor( list, pointer ) +{ +} + +template +inline +FindCursor::FindCursor( const Cursor& cursor ) : + Cursor( cursor ) +{ +} + +template +Cursor +FindCursor::operator =( const Cursor& c ) +{ + ( (Cursor&)*this ) = c; + return *this; +} + +template +inline FindCursor +FindCursor::find( const T& thing ) +{ + Cursor c( *this ); + while( c.forward() ) + { + T& look = *c; + if ( U::equal( look, thing ) ) + return c; + c++; + } + + return FindCursor( *this ); // cursor is not .ok() +} diff --git a/flower/handle.hh b/flower/handle.hh new file mode 100644 index 0000000000..32573998f2 --- /dev/null +++ b/flower/handle.hh @@ -0,0 +1,57 @@ +#ifndef HANDLE_HH +#define HANDLE_HH +/// reference counting handle +template +class Handle { + T *obj; + int *refs; + + /// let go of ref. Delete if necessary + void down() { + if (!(*refs--)) { + delete obj; + delete refs; + } + obj = 0; + refs = 0; + } + /// point to new object. + void up(T *t, int *r=0) { + if (!r) { + refs = new int; + *refs = 1; + } else { + refs =r; + *refs++; + } + obj = t; + } + /// POST: *refs == 1 + void copy() { + if(*refs != 1){ + T * newobj = new T(*obj ); + down(); + up(newobj); + } + } + Handle(Handle const &src) { + up(src.obj, src.refs); + } + Handle(T & o) { + up (&o); + } + void operator=(Handle const& src) { + if (this == &src) + return; + down(); + up(src.o, src.refs); + } + operator const T&() { + return *obj; + } + operator T&() { + copy(); + return *obj; + } +} +#endif diff --git a/flower/lgetopt.cc b/flower/lgetopt.cc new file mode 100644 index 0000000000..ac9a12d035 --- /dev/null +++ b/flower/lgetopt.cc @@ -0,0 +1,214 @@ +/* + process command line, GNU style. + + + this is (Copyleft) 1996, Han-Wen Nienhuys, + */ +#include +#include +#include +#include "lgetopt.hh" + +long +Getopt_long::intarg() { + long l; + if (sscanf(optarg, "%ld", &l) != 1) + report(E_ILLEGALARG); + + return l; +} +long_option_init * +Getopt_long::parselong() { + const char *optnm = argv[optind] + 2 ; + assert(*optnm); + + char *endopt = strchr(optnm, '='); + int searchlen = (endopt) ? endopt - optnm : strlen(optnm); + + beet=0; + for (int i=0; i< table_len; i++) { + const char *ln = the_opts[i].longname; + + if (ln && !strncmp(ln, optnm, searchlen)) { + beet = the_opts+i; + break; + } + } + + if (!beet) { + report(E_UNKNOWNOPTION); + return 0; + } + optind++; + optindind = 0; + + + if (beet->take_arg) { + if (endopt) + optarg = endopt +1; // a '=' + else { + optarg = argv[optind]; + optind++; + } + if (!optarg) + report(E_ARGEXPECT); + + } else { + optarg = 0; + if (endopt) + report(E_NOARGEXPECT); + } + + return beet; +} + + +ostream & +long_option_init::printon(ostream &errorout) +{ + if (shortname) + errorout <<"-" << shortname; + if (shortname && longname) + errorout << ", "; + if (longname) + errorout << "`--" << longname << "'"; + return errorout; +} + +// report an error, GNU style. +void +Getopt_long::report(Errorcod c) +{ + error = c; + if (!errorout) + return; + + *errorout << argv[0] << ": "; + switch (c) { + case E_ARGEXPECT: + *errorout<< "option "; + beet->printon(*errorout); + *errorout << "requires an argument"<longname << "' does not allow an argument"<printon(*errorout); + *errorout << '\n'; + default: + assert(false); + } + exit(2); +} + +long_option_init * +Getopt_long::parseshort() { + char c=argv[optind][optindind]; + beet=0; + assert(c); + + for (int i=0; i < table_len; i++) + if (the_opts[i].shortname == c) { + beet = the_opts+i; + break; + } + + if (!beet){ + report(E_UNKNOWNOPTION); + return 0; + } + + optindind++; + if (!beet->take_arg){ + optarg = 0; + return beet; + } + optarg = argv[optind] + optindind; + + optind ++; + optindind = 0; + + if (!optarg[0]) { + optarg = argv[optind]; + optind ++; + } + if (!optarg) { + report(E_ARGEXPECT); + } + + return beet; +} + +long_option_init * +Getopt_long::operator()() { + if (!next()) + return 0; + + if (optindind) + return parseshort(); + + if (argv[optind][0] != '-') + return 0; + + if (argv[optind][1] == '-') {// what to do with "command -- bla" + return parselong(); + } else { + optindind = 1; + return parseshort(); + } +} + +Getopt_long::Getopt_long(int c, char **v, long_option_init *lo) { + the_opts = lo; + errorout = &cerr; + argv = v; + argc = c; + optind = 1; + optindind = 0; + + // reached end of option table? + int i; + for (i = 0; the_opts[i].longname ||the_opts[i].shortname; i++) + ; + table_len = i; +} + +bool Getopt_long::next() { + error = E_NOERROR; + while (optind < argc && !argv[optind][optindind]) { + optind++; + optindind = 0; + } + return (optind < argc); +} + +char * +Getopt_long::current_arg() +{ + if (optind >= argc) + return 0; + char * a = argv[optind]; + return a + optindind; +} + +char * +Getopt_long::get_next_arg() +{ + char * a = current_arg(); + if ( a) { + optind ++; + optindind = 0; + } + return a; +} diff --git a/flower/lgetopt.hh b/flower/lgetopt.hh new file mode 100644 index 0000000000..0ff3e08419 --- /dev/null +++ b/flower/lgetopt.hh @@ -0,0 +1,90 @@ +#ifndef LGETOPT_HH +#define LGETOPT_HH + +#include + + +class ostream; + +struct long_option_init { + bool take_arg; + const char* longname; + char shortname; + + ostream &printon(ostream &errorout); +}; + + +/// +class Getopt_long { +public: + /// + enum Errorcod { E_NOERROR = 0, E_ARGEXPECT, E_NOARGEXPECT, E_UNKNOWNOPTION, + E_ILLEGALARG } ; + + /** errorcodes: no error, argument expected, no argument expected, + unknown option, illegal argument (eg. int expected). */ + +private: + + /// the option info. + long_option_init *the_opts; + int table_len; + + /// if doing short option, argv[optind][optindind] is processed next. + int optindind; + + /// the option found + long_option_init *beet; + + /// get ready for processing next error. + bool next(); + long_option_init *parselong(); + long_option_init *parseshort(); + + ostream *errorout; + + /// report an error and abort + void report(Errorcod c); +public: + /// what to do with errors + void seterror(ostream *os); + /** + report messages on #*os#, and abort. + if #os# is null, then do not report nor abort, just set #error# + */ + + /// argument. Set to 0 if not present + char* optarg; + + + /// return an integer (with err. detect) + long intarg(); + /// argv[optind] will be processed next. + int optind; + + /// the arguments + char **argv; + + /// the arg. count + int argc; + + /// construct: pass arguments and option info. + Getopt_long(int c, char **v, long_option_init *lo); + + /// get the next option + long_option_init *operator()(); + /** + RETURN: pointer to next option found. + 0 if error occurred, or next argument is no option. + */ + + char *current_arg(); + char * get_next_arg(); + Errorcod error; +}; +/** + C++ for version of long_getopt. For processing GNU style command line arguments. + No pointer (return values, arguments) contents are copied. + */ +#endif diff --git a/flower/link.hh b/flower/link.hh new file mode 100644 index 0000000000..6f9c75a411 --- /dev/null +++ b/flower/link.hh @@ -0,0 +1,38 @@ +// link.hh + +#ifndef __LINK_HH +#define __LINK_HH +template +class List; + + +/// class for List +template +class Link +{ +// friend class Cursor; +public: + Link( const T& thing ); + + Link* previous(); + Link* next(); + + /// put new Link item after me in list + void add( const T& thing ); + /// put new Link item before me in list + void insert( const T& thing ); + void remove(List &l); + + T& thing(); + void OK() const; +private: + Link( Link* previous, Link* next, const T& thing ); + + T thing_; + Link* previous_; + Link* next_; +}; + +#include "link.inl" + +#endif // __LINK_HH // diff --git a/flower/link.inl b/flower/link.inl new file mode 100644 index 0000000000..e76caab4f1 --- /dev/null +++ b/flower/link.inl @@ -0,0 +1,100 @@ +// link.inl -*-c++-*- +#ifndef LINK_INL +#define LINK_INL +#include +template +inline +void +Link::OK() const +{ + if (previous_) { + assert(previous_->next_ == this); + } + if (next_) { + assert(next_->previous_ == this); + } +} + +template +inline +Link::Link( const T& thing ) : + thing_( thing ) +{ + previous_ = next_ = 0; +} + +template +inline +Link::Link( Link* previous, Link* next, const T& thing ) : + thing_( thing ) +{ + previous_ = previous; + next_ = next; +} + +template +inline +Link* +Link::next() +{ + return next_; +} + +template +inline +Link* +Link::previous() +{ + return previous_; +} + +template +inline +void +Link::add( const T& thing ) +{ + + Link* l = new Link( this, next_, thing ); + if ( next_ ) + next_->previous_ = l; + next_ = l; +} + +template +inline void +Link::insert( const T& thing ) +{ + // Link* l = new Link( next_, this, thing ); + // bugfix hwn 16/9/96 + Link* l = new Link( previous_, this, thing ); + if ( previous_ ) + previous_->next_ = l; + previous_ = l; +} + +/* + don't forget to adjust #l#'s top_ and bottom_. + */ +template +inline void +Link::remove(List &l) +{ + if ( previous_ ) + previous_->next_ = next_; + else + l.top_ = next_; + + if ( next_ ) + next_->previous_ = previous_; + else + l.bottom_ = previous_; +} + +template +inline +T& +Link::thing() +{ + return thing_; +} +#endif diff --git a/flower/list.cc b/flower/list.cc new file mode 100644 index 0000000000..f93352f507 --- /dev/null +++ b/flower/list.cc @@ -0,0 +1,88 @@ +#ifndef LIST_CC +#define LIST_CC + +#include "list.hh" + +template +void +List::OK() const +{ + int i = size_; + Link *lp = top_; + while (i--) { + assert(lp); + lp->OK(); + lp = lp->next(); + } + assert(!lp); + i = size_; + lp = bottom_; + while (i--) { + assert(lp); + lp->OK(); + lp = lp->previous(); + } + assert(!lp); +} + +template +Cursor +List::top() +{ + + // ?? waarvoor is deze if ? + if ( top_ ) // equivalent: if ( size_ ) + { + Link* t = top_->previous(); + assert( t != top_ ); // silly link + while ( t ) + { + top_ = t; + t = top_->previous(); + } + } + // list empty: Cursor not ok() + return Cursor( *this, top_ ); +} + + +template +Cursor +List::bottom() +{ + /* wat is dit voor zooi? kan dit niet weg? + + (invarianten!) + */ + if ( bottom_ ) // equivalent: if ( size_ ) + { + Link* b = bottom_->next(); + assert( b != bottom_ ); // silly link + while ( b ) + { + bottom_ = b; + b = bottom_->next(); + } + } + // list empty: Cursor not ok() + return Cursor( *this, bottom_ ); +} + + +// not inlined since it assumes knowledge of destructor. +template +inline void +PointerList::remove( Cursor me ) +{ + if ( me.ok() ) + { + + delete *me; + List::remove( me ); + } +} + + + + +#endif diff --git a/flower/list.hh b/flower/list.hh new file mode 100644 index 0000000000..97e672f2e0 --- /dev/null +++ b/flower/list.hh @@ -0,0 +1,96 @@ +// list.hh + +#ifndef __LIST_HH +#define __LIST_HH + +class ostream; +template class Cursor; +template class Link; + +/// all purpose list +template +class List +{ + public: + /// construct empty list + List(); + + /// construct list from first item. + List( const T& thing ); + + virtual ~List(); + + Cursor bottom(); + + int size() const; + Cursor top(); + void OK() const; + protected: + friend class Cursor; + friend class Link; + + /// add after after_me + void add( const T& thing, Cursor after_me ); + + /// put thing before #before_me# + void insert( const T& thing, Cursor before_me ); + virtual void remove( Cursor me ); + + int size_; + Link* top_; + Link* bottom_; +}; +/** + a doubly linked list; + List can be seen as all items written down on paper, + from top to bottom + + class Cursor is used to extend List + + items are always stored as copies in List, but: + #List# : copies of #String# stored + #List# : copies of #String*# stored! + (do not use, use \Ref{PointerList}## instead.) + + {\bf note:} + retrieving "invalid" cursors, i.e. + #top()/bottom()# from empty list, #find()# without success, + results in a nonvalid Cursor ( #!ok()# ) + + + INVARIANTEN! +*/ + + +/// Use for list of pointers, e.g. PointerList. +template +class PointerList : public List +{ + public: + PointerList(); + PointerList( const T& thing ); + + /// + virtual ~PointerList(); + /** + This function deletes deletes the allocated pointers of all links. + #\Ref{~List}# is used to delete the links themselves. + */ + + protected: + virtual void remove( Cursor me ); +}; + +#include "list.inl" +#include "cursor.hh" + +// instantiate a template: explicit instantiation. +#define L_instantiate(a) template class List; template class Cursor; \ + template class Link +#define PL_instantiate(a) L_instantiate(a *); template class PointerList + +#endif // __LIST_HH // + + + + diff --git a/flower/list.inl b/flower/list.inl new file mode 100644 index 0000000000..54df457481 --- /dev/null +++ b/flower/list.inl @@ -0,0 +1,144 @@ +// -*-c++-*- +#ifndef LIST_INL +#define LIST_INL +template +inline +List::List() +{ + top_ = bottom_ = 0; + size_ = 0; +} + +template +inline +List::List( const T& thing ) +{ + top_ = bottom_ = 0; + size_ = 0; + add( thing, Cursor( *this, bottom_ ) ); +} + +template +inline +List::~List() +{ + for ( Cursor c( *this ); c.forward(); c++ ) + remove( c ); +} + +template +inline void +List::add( const T& thing, Cursor after_me ) +{ +#if 0 + if ( after_me.ok() ) + after_me.pointer()->add( thing ); + else if ( size_ ) + bottom().pointer()->add( thing ); + else + bottom_ = top_ = new Link( thing ); +#endif + + if (!size_) { // not much choice if list is empty + bottom_ = top_ = new Link( thing ); + } else { // add at aprioprate place + Link *p = ( after_me.ok() ) ? + after_me.pointer() : bottom().pointer(); + p->add(thing); + if (p == bottom_) // adjust bottom_ if necessary. + bottom_ = p->next(); + } + + size_++; +} +/** + + Procedure: + \begin{itemize} + \item if #after_me# is #ok()#, add after #after_me#, else + \item if list !empty simply add to bottom, else + \item list is empty: create first \Ref{Link} and initialize + #bottom_# and #top_#. + \end{itemize} +*/ + +template +inline void +List::insert( const T& thing, Cursor before_me ) +{ + if (!size_) { + bottom_ = top_ = new Link( thing ); + } else { + Link *p = + (before_me.ok())? + before_me.pointer() : top().pointer(); + + p->insert(thing); + if (p == top_) + top_ = p->previous(); + } + + size_++; +#if 0 // rewrite hwn 16/9 + if ( before_me.ok() ) + before_me.pointer()->insert( thing ); + else if ( size_ ) + top().pointer()->insert( thing ); + else + bottom_ = top_ = new Link( thing ); + size_++; +#endif +} + +template +inline void +List::remove( Cursor me ) +{ + if ( me.ok() ) + { + me.pointer()->remove(*this); + delete me.pointer(); + size_--; + } +} + +template +inline int +List::size() const +{ + return size_; +} + +template +inline +PointerList::PointerList() : + List() +{ +} + +template +inline +PointerList::PointerList( const T& thing ) : + List( thing ) +{ +} + +template +inline +PointerList::~PointerList() +{ + for ( Cursor c( *this ); c.forward(); c++ ) + remove( c ); +} + +template +inline void +PointerList_print( PointerList const & l ) +{ + List& promises_to_be_const = (List&) l; + for ( Cursor c( promises_to_be_const ); c.forward(); c++ ) + (*c)->print(); +} + + +#endif diff --git a/flower/string.cc b/flower/string.cc new file mode 100644 index 0000000000..da4941da7b --- /dev/null +++ b/flower/string.cc @@ -0,0 +1,395 @@ +/**************************************************************************** + PROJECT: FlowerSoft C++ library + FILE : string.cc + + Rehacked by HWN 3/nov/95 + removed String & + introduced Class String_handle +--*/ + +#include +#include +#include +#include +#include +//#include "globals.hh" +#include "string.hh" + +char* strlwr( char* s ) +{ + char* p = s; + + while( *p ) + { + *p = tolower( *p ); /* a macro on some compilers */ + p++; + } + return s; +} + +char* strupr( char* s ) +{ + char* p = s; + + while( *p ) + { + *p = toupper( *p ); /* a macro on some compilers */ + p++; + } + return s; +} + +// return array, alloced with new. +char * +String::copy_array() const +{ + const char *src = data; + char *dest = new char[data.len() + 1]; + strcpy(dest, src); + return dest; +} + +void +String::printOn(ostream& os) const +{ + os << (const char*) data; +} + +String::String (bool b) +{ + *this = (const char *) (b ? "true" : "false"); +} +String::String( const char* source ) +{ + assert(source); + data = source; +} + +void +String::operator +=(String s) +{ + *this += (const char *) s; +} + +int +String::len() const +{ + return data.len(); +} + +String::String(char c, int n) +{ + int l = n; + assert(n >= 0 && n <= 80); // what the fuck is 80? +//min(max( n, 0 ), 80); + char s[81]; + memset(s, c, l); + s[l] = 0; + data = s; +} + +String::String(int i) +{ + char digits[ 81 ]; // who the FUCK is 80??? + digits[ 0 ] = '\0'; + sprintf(digits, "%d", i ); // assume radix 10 + data = digits; +} + +String::String( const int i, const int n, const char c ) +{ + char fillChar = c; + if ( fillChar) + fillChar = '0'; + + String v( i ); + + data = String( fillChar, n - v.len() ) + String( v ); + // String convd to const char * +} + + const char* +String::ptr() const +{ + return data; +} + + + +#ifdef CENTRAL_OBJECT // everything derived from Sortable object +// comparisons. +int +String::operator ==( const Sortable& test ) const +{ + const String *s = (const String *) &test; + return *this == *s; +} + +int +String::operator &&(const Object& test) const +{ + const String *s = (const String *) &test; + + int i = min( len(), s->len() ); + return ( i > 0 ) ? + ( !strncmp( data, s->data, i ) ) : 0; +} + +int +String::operator >( const Sortable& test ) const +{ + const String *s = (const String *) &test; + return strcmp( data, s->data ) > 0; +} +#endif + +// signed comparison, analogous to strcmp; +int +String::compare( const char* test ) const +{ + if (test == (const char *) data) + return 0; + + return strcmp(data, test); +} + + +int +String::lastPos( const char c ) const +{ + const char *me = data; + int pos = 0; + if ( len() ) + { + const char* p = strrchr(me, c ); + if ( p ) + pos = p - me + 1; + } + return pos; +} + +int +String::lastPos( const char* string ) const +{ + int pos = 0; + int length = strlen( string ); + if ( len() && length ) + { + int nextpos = this->pos( string ); + while( nextpos ) + { + pos += nextpos; + nextpos = right( len() - pos - length + 1 ).pos( string ); + } + } + return pos; +} + +// find c +// return 0 if not found. + +// ? should return len()?, as in string.left(pos(delimiter)) +int +String::pos(char c ) const +{ + const char *me = data; + int pos = 0; + if ( len() ) + { + const char* p = strchr( me, c ); + if ( p ) + pos = p - me + 1; + } + return pos; +} + +// find searchfor. (what if this == "" && searchfor == "") ??? +int +String::pos( const char* searchfor ) const +{ + const char *me = data; + int pos = 0; + if ( len() && searchfor) + { + const char* p = strstr(me, searchfor); + if ( p ) + pos = p - me + 1; + } + return pos; +} + +// find chars of a set. +int +String::posAny( const char* string ) const +{ + int pos = 0; + const char *s = (const char *)data; + if ( len() && string ) + { + const char* p = strpbrk( s, string ); + if ( p ) + pos = p - s + 1; + } + return pos; +} + +String +String::left( int n ) const +{ + if (n >= len()) + return *this; + + String retval; + if (n < 1) + return retval; + + retval = *this; + retval.data.trunc(n); + return retval; +} + + +// n rightmst chars +String +String::right( int n ) const +{ + if (n > len()) + return *this; + + String retval; + if ( n < 1) + return retval; + + const char *src = (const char *)data + len() - n; + retval += src; + + return retval; +} + + +String +String::nomid( const int pos, const int n ) const +{ + String retval; + + if ( pos < 1 ) + return String(""); + if ( pos > len()) + return *this; + + return String( String( left( pos - 1 ) ) + right( len() - pos - n + 1 )); +} + + +String +String::mid( int pos, int n ) const +{ + String retval; + + // HWN. This SUX: + // pos 1 == data->string[ 0 ]; + // pos 0 allowed for convenience + if ( !len() || ( pos < 0 ) || ( pos > len() ) && ( n < 1 ) ) + return retval; + + retval = ((const char *) data) + pos -1; + if (n > retval.len()) + n =retval.len(); + retval.data.trunc(n); + return retval; +} + + +// to uppercase +String +String::upper() +{ + char *s = data.array_for_modify(); + strupr(s ); + return *this; +} + + +// to lowercase +String String::lower() +{ + char *s = data.array_for_modify(); + strlwr(s); + return *this; +} + +String::String (double f, const char *fmt) +{ + char buf[100]; // ugly + if (!fmt) + sprintf(buf, "%f", f); + else + sprintf(buf, fmt,f); + *this = buf; +} + +long +String::value() const +{ + long l =0; + if (len()) { + int conv = sscanf(data, "%ld", &l); + assert(conv); + } + return l; +} + +double +String::fvalue() const +{ + double d =0; + if (len()) { + int conv = sscanf(data, "%lf", &d); + assert(conv); + } + return d; +} + + +String quoteString( String msg, String quote) +{ + return msg + " `" + quote + "' "; +} + + +char *strrev(char *s) +{ + char c; + char *p = s; + char *q = s + strlen(s) - 1; + + while (q > p) { + c = *p; + *p++ = *q; + *q-- = c; + } + return s; +} + + +String +String::reversed() const +{ + String retval=*this; + char *s = retval.data.array_for_modify(); + strrev(s); + return retval; +} +bool +String::to_bool() const +{ + if (!len()) + return false; + if (*this == "0") + return false; + String u (*this); + u.upper(); + if (u== "FALSE") + return false; + return true; +} diff --git a/flower/string.hh b/flower/string.hh new file mode 100644 index 0000000000..d3d2774261 --- /dev/null +++ b/flower/string.hh @@ -0,0 +1,172 @@ +/* + + FILE : string.hh -- implement String inline helper classes, + and declare stringclass. + + + Rehacked by HWN 3/nov/95 + removed String & 's + introduced Class String_handle + */ + +#ifndef STRING_HH +#define STRING_HH + + +#include +#include + + +#include "stringutil.hh" + +/// the smart string class. +class String +{ +protected: + String_handle data; // should derive String from String_handle? + +public: + /// init to "" + String() { } + /** needed because other constructors are provided.*/ + + /// String s = "abc"; + String( const char* source ); + + /// "ccccc" + String( char c, int n = 1 ); + + /// String s( 10 ); + String( int i ); + + /// 'true' or 'false' + String(bool ); + + /// String s( 3.14, 6, '#' ); + String ( double f , const char *fmt =0); + String( int i, int n, char c = ' ' ); + + /// return a "new"-ed copy of contents + char *copy_array() const; // return a "new"-ed copy of contents + + const char *ptr() const; + /// return the data. Don't use for writing the data. + operator const char *() const { return ptr(); } + + String operator =( const String & source ) { data = source.data; return *this; } + + /// concatenate s + void operator += (const char *s) { data += s; } + void operator += (String s); + + char operator []( int n ) const { return data[n]; } + + /// return n leftmost chars + String left( int n ) const; + + /// return n rightmost chars + String right( int n ) const; + + /// convert this to upcase + String upper(); + + /// convert this to downcase + String lower(); // & ?? + + /// return the "esrever" of *this + String reversed() const; + + + /// return a piece starting at pos (first char = pos 1), length n + String mid(int pos, int n ) const; + + /// cut out a middle piece, return remainder + String nomid(int pos, int n ) const; + + /// signed comparison, analogous to strcmp; + int compare( const char* s ) const; + + /// index of rightmost c + int lastPos( char c) const; + /// index of rightmost element of string + int lastPos( const char* string ) const; + + /// index of leftmost c + int pos(char c ) const; + /** + RETURN: + 0 if not found, else index + 1 + */ + int pos(const char* string ) const; + int posAny(const char* string ) const; + + + /// provide Stream output + void printOn(ostream& os) const; + + /// convert to an integer + long value() const; + + /// convert to a double + double fvalue() const; + + /// the length of the string + int len() const; + + /// DO NOT MAKE THIS INTO AN OPERATOR + bool to_bool() const; + /** perl -like string to bool conversion + */ +}; +/** + + Intuitive string class. provides + + ref counting thru #String_handle# + + conversion from bool, int, double, char *, char. + + conversion to int, upcase, downcase + + + printable. + + indexing (pos, posAny, lastPos) + + cutting (left, right, mid) + + concat (+=, +) + + signed comparison (<, >, ==, etc) + + No operator[] is provided, since this would be enormously slow. If needed, + convert to const char *. +*/ + + +// because const char* also has an operator ==, this is for safety: +inline bool operator==(String s1, String s2){ return !(s1.compare(s2));} +inline bool operator==(String s1, const char *s2){ return !(s1.compare(s2));} +inline bool operator==(const char *s1, String s2){ return !(s2.compare(s1));} +inline bool operator!=(String s1, const char *s2 ) { return s1.compare(s2);} +inline bool operator!=(const char *s1,String s2) { return s2.compare(s1);} +inline bool operator!=(String s1, String s2 ) { return s1.compare(s2);} + +inline String +operator + (String s1, String s2) +{ + s1 += s2; + return s1; +} + +inline ostream & +operator << ( ostream& os, String d ) +{ + d.printOn(os); + return os; +} + + +String quoteString(String message, String quote); + +#endif diff --git a/flower/stringutil.hh b/flower/stringutil.hh new file mode 100644 index 0000000000..b7503b7d2a --- /dev/null +++ b/flower/stringutil.hh @@ -0,0 +1,245 @@ +#ifndef STRINGUTIL_HH +#define STRINGUTIL_HH +#include + +#ifndef EVELYN +#define NDEBUG BLONDE + // switching into "blonde" mode + // i trust stringclass nowadays. +#endif + +class String_handle; +/// Internal String struct +class StringData { + // GNU malloc: storage overhead is 8 bytes anyway. + const int INITIALMAX = 8; + +friend class String_handle; + int maxlen; // maxlen is arraysize-1 + + int length; + char* string; + int references; + + /// init to "" + StringData() { + references=0; + maxlen = INITIALMAX; + string = new char[maxlen + 1]; + string[0] = 0; + length = 0; + } + + /// init from src. Conservative allocation. + StringData(StringData const &src) { + references=0; + maxlen = length = src.length; + string = new char[maxlen+1]; // should calc GNU 8byte overhead. + strcpy(string, src.string); + } + + ~StringData() { + assert(references == 0); + delete[] string; + } + + + void setmax(int j) { + OKW(); + if (j > maxlen) { + delete string; + maxlen = j; + string = new char[maxlen + 1]; + + string[0] = 0; + length = 0; + } + } + /** POST: maxlen >= j. + IN: j, maximum stringlength. + contents thrown away. + */ + /// + void remax(int j) { + OKW(); + if (j > maxlen) { + maxlen = j; + char *p = new char[maxlen + 1]; + strcpy(p,string); + delete[] string; + string = p; + // length = strlen(string); + } + } + /** POST: maxlen >= j. + IN: j, maximum stringlength. + contents are kept if it grows. + */ + /// check if writeable. + void OKW() { + + assert (references == 1); + + } + + /// check state. + void OK() { + assert(strlen(string) == size_t(length)); + assert(maxlen >= length); + assert(bool(string)); + assert(references >= 1); + } + + // needed? + void update() { + length = strlen (string); + } + + /// reduce memory usage. + void tighten() { // should be dec'd const + maxlen = length; + char *p = new char[maxlen + 1]; + strcpy(p,string); + delete[] string; + string = p; + } + + // assignment. + void set(const char *s) { + OKW(); + + assert(s); + + length = strlen (s); + remax(length); + strcpy(string,s); + } + + /// concatenation. + void operator += (const char *s) { + OK(); + OKW(); + int old = length; + + length += strlen(s); + remax (length); + strcpy(string + old, s); + } + + /// the array itself + operator const char *() const { return string; } + + // idem, non const + char *array_for_modify() { + OKW(); + return string; + } + void trunc(int j) { + OKW(); + assert(j >= 0 && j <= length); + string[j] = 0; + length = j; + } + + /** not really safe. Can alter length without StringData knowing it. + */ + char &operator [](int j) { + assert(j >= 0 && j <= length); + return string[j] ; + } + + char operator [](int j) const { + assert(j >= 0 && j <= length); + return string[j]; + } +}; + +/** + the data itself. Handles simple tasks (resizing, resetting) + */ +/****************************************************************/ +/// ref. counting for strings +class String_handle { + StringData* data; + + /// decrease ref count. Named kind of like a Tanenbaum semafore + void down() { if (!(--data->references)) delete data; data = 0; } + + /// increase ref count + void up(StringData *d) { data=d; data->references ++; } + + /** make sure data has only one reference. + POST: data->references == 1 + */ + void copy() { + if (data->references !=1){ + StringData *newdata = new StringData(*data); + down(); + up(newdata); + } + } + +public: + + String_handle() { + up(new StringData); + } + ~String_handle() { + down(); + } + String_handle(String_handle const & src) { + up(src.data); + } + + /// retrieve the actual array. + operator const char *() const { return *data; } + char *array_for_modify() { + copy(); + return data->array_for_modify(); + } + + void operator =(String_handle const &src) { + if (this == &src) + return; + down(); + up(src.data); + } + + void operator += (const char *s) { + copy(); + *data += s; + } + + + char operator[](int j) const { return (*data)[j]; } + + // !NOT SAFE! + // don't use this for loops. Use array_for_modify() + char &operator[](int j) { + copy(); // hmm. Not efficient + return data->array_for_modify()[j]; + } + + void operator = (char const *p) { + copy(); + data->set(p); + } + + void trunc(int j) { copy(); data->trunc(j); } + int len() const { return data->length; } +}; +/** + handles ref. counting, and provides a very thin + interface using char * + */ + + +#ifdef NDEBUG +#if (NDEBUG == BLONDE) +#undef NDEBUG +#endif +#endif + + + +#endif // STRINGUTIL_HH diff --git a/flower/textdb.cc b/flower/textdb.cc new file mode 100644 index 0000000000..4c10139583 --- /dev/null +++ b/flower/textdb.cc @@ -0,0 +1,48 @@ +#include "textdb.hh" +bool +Text_db::eof() +{ + Data_file::gobble_leading_white(); + return Data_file::eof(); +} + +void +Text_db::gobble_leading_white() +{ + while (1) { + Data_file::gobble_leading_white(); + if (eof()) + return ; + char c; + if ((c = data_get()) !='\n'){ + data_unget (c); + return ; + } + } +} + + +Text_record +Text_db::get_record() +{ + while (1) { + String s; + svec fields; + assert(!eof()); + + while ((s = get_word()) != "") + { + fields.add(s); + gobble_white(); + } + + + if (get_line() != "") + assert(false); + + assert (fields.sz()); + return Text_record(fields, get_name(), line()); + } +} + + diff --git a/flower/textdb.hh b/flower/textdb.hh new file mode 100644 index 0000000000..bea0c4521e --- /dev/null +++ b/flower/textdb.hh @@ -0,0 +1,57 @@ +#ifndef TEXTDB_HH +#define TEXTDB_HH + +#include "textstr.hh" + +/**: do "#" comments, read quote enclosed fields */ + +/// a "const" svec. Contents can't be changed. +class Text_record : svec +{ + int line_no; + String filename; + +public: + Text_record() { } // needed because of other ctor + + /// report an error in this line. + message(String s) { + cerr << '\n'<< filename << ": "<< line_no << s << "\n"; + } + String operator[](int j) { + return svec::operator[](j); + } + + Text_record(svec s, String fn, int j) : svec(s) { + filename = fn; line_no = j; + } + svec::sz; +}; + +/// abstraction for a datafile +class Text_db : private Data_file +{ + void gobble_leading_white(); +public: + /// get a line with records + Text_record get_record(); + + Text_db(String fn):Data_file(fn) { } + Data_file::error; + bool eof(); + + /// get next line. + Text_record operator++(int) { + return get_record(); + } + /// are we done yet? + operator bool() { + return !eof(); + } +}; + + +/** + add a subrec/fieldsep/record separator + */ +#endif diff --git a/flower/textstr.hh b/flower/textstr.hh new file mode 100644 index 0000000000..41a979f27f --- /dev/null +++ b/flower/textstr.hh @@ -0,0 +1,114 @@ +#include +#include + +//#include "globals.hh" +#include "string.hh" +#include "vray.hh" + +/// line counting input stream. +class Text_stream +{ + int line_no; + + // could just have used streams. + FILE *f; + sstack pushback; + String name; + + public: + Text_stream(String fn); + String get_name() { return name; } + bool eof() { + return feof(f); + } + bool eol() { + return (peek() == '\n'); + } + char peek() { + char c = get(); + unget(c); + return c; + } + int line(){ + return line_no; + } + + char get() { + char c; + + if (pushback.empty()) + c = getc(f); + else + c = pushback.pop(); + + if (c =='\n') + line_no++; + return c; + } + void unget(char c) { + if (c =='\n') + line_no--; + pushback.push(c); + } + ~Text_stream (){ + if (!eof()) + cerr <<__FUNCTION__<< ": closing unended file"; + + fclose(f); + } + + /// GNU format message. + void message(String s); +}; +/** + a stream for textfiles. linecounting. Thin interface getchar and + ungetchar. (ungetc is unlimited) + + should protect get and unget against improper use +*/ + + +/// read a data file +class Data_file : private Text_stream +{ + + public: + bool rawmode; + + Text_stream::line; + Text_stream::eof; + Text_stream::get_name; + + char data_get(); + void data_unget(char c) { + unget(c); + } + + /// read line, eat #\n# + String get_line(); + + /// read a word till next space, leave space. Also does quotes + String get_word(); + + /// gobble horizontal white stuff. + void gobble_white(); + + /// gobble empty stuff before first field. + void gobble_leading_white(); + Data_file(String s) : Text_stream(s) { + //*mlog << "(" << s << flush; + rawmode= false; + } + + ~Data_file() { + // *mlog << ")"< classes; + +}; +/* + Union find, a standard algorithm: + + Union_find represents an undirected graph of N points. You can + connect two points using #connect()#. #find(i)# finds a uniquely + determined representant of the equivalence class of points + connected to #i#. + + */ +#endif diff --git a/flower/vray.hh b/flower/vray.hh new file mode 100644 index 0000000000..d56cad00d6 --- /dev/null +++ b/flower/vray.hh @@ -0,0 +1,171 @@ +/* + (c) Han-Wen Nienhuys 1995,96 + + Distributed under GNU GPL +*/ + +#ifndef SVEC_H +#define SVEC_H +#include + +/// copy a bare (C-)array from #src# to #dest# sized #count# +template +inline void arrcpy(T*dest, T*src, int count) { + for (int i=0; i < count ; i++) + *dest++ = *src++; +} + +///scaleable array template, for T with def ctor. +template +class svec { +protected: + + int max; + + /// the data itself + T *thearray; + + /// stretch or shrink array. + void remax(int newmax) { + T* newarr = new T[newmax]; + size = (newmax < size) ? newmax : size; + arrcpy(newarr, thearray, size); + + delete[] thearray; + thearray = newarr; + max = newmax; + } + int size; + +public: + /// check invariants + void OK() const { + assert(max >= size && size >=0); + if (max) assert(thearray); + } + /// report the size. See {setsize} + int sz() const { return size; } + + /// POST: sz() == 0 + void clear() { size = 0; } + + svec() { thearray = 0; max =0; size =0; } + /// set the size to #s# + void set_size(int s) { + if (s >= max) remax(s); + size = s; + } + /** POST: sz() == s. + Warning: contents are unspecified */ + + ~svec() { delete[] thearray; } + + /// return a "new"ed copy of array + T* copy_array() const { + T* Tarray = new T[size]; + arrcpy(Tarray, thearray, size); + return Tarray; + } + // depracated + operator T* () const { + return copy_array(); + } + void operator=(svec const & src) { + set_size (src.size); + arrcpy(thearray,src.thearray, size); + } + svec(const svec & src) { + thearray = src.copy_array(); + max = size = src.size; + } + + /// tighten array size. + void precompute () { remax(size); } + + /// this makes svec behave like an array + T &operator[] (const int i) const { + assert(i >=0&&i=0 && j<= size); + set_size(size+1); + for (int i=size-1; i > j; i--) + thearray[i] = thearray[i-1]; + thearray[j] = k; + } + void del(int i) { + assert(i >=0&& i < size); + arrcpy(thearray+i, thearray+i+1, size-i-1); + size--; + } + // quicksort. + void sort (int (*compare)(T& , T& ), + int lower = -1, int upper = -1 ) { + if (lower < 0) { + lower = 0 ; + upper = sz(); + } + if (lower >= upper) + return; + swap(lower, (lower+upper)/2); + int last = lower; + for (int i= lower +1; i <= upper; i++) + if (compare(thearray[i], thearray[lower]) < 0 ) + swap( ++last,i); + swap(lower, last); + sort(compare, lower, last-1); + sort(compare, last+1, lower); + } +}; +/** + + This template implements a scaleable vector. With (or without) range + checking. It may be flaky for objects with complicated con- and + destructors. The type T should have a default constructor. It is + best suited for simple types, such as int, double or String + + */ + +/// A simple stack based on svec. +template +class sstack : svec { + public: + T top() { return last(); } + T pop() { + assert(!empty()); + T l = last(); + svec::pop(); + return l; + } + void push(T l) { add(l); } + bool empty() { return svec::empty(); } +}; +/** + Same as for #svec# goes here. +*/ +#endif diff --git a/glob.hh b/glob.hh new file mode 100644 index 0000000000..7b8bb557d4 --- /dev/null +++ b/glob.hh @@ -0,0 +1,9 @@ +#ifndef GLOB_HH +#define GLOB_HH +#include +#include +#include "real.hh" + +#include "proto.hh" +#include "const.hh" +#endif diff --git a/globvars.hh b/globvars.hh new file mode 100644 index 0000000000..c79e017f71 --- /dev/null +++ b/globvars.hh @@ -0,0 +1 @@ +extern Score *the_score; diff --git a/item.cc b/item.cc new file mode 100644 index 0000000000..5856156a66 --- /dev/null +++ b/item.cc @@ -0,0 +1,49 @@ +#include "line.hh" + + +#include "cols.hh" +String +Spanner::TeXstring() const +{ + assert(right->line); + Real w = left->hpos - right->hpos; + return (*strets)(w); +} + +Spanner * +Spanner::broken_at(const PCol *c1, const PCol *c2) const +{ + Spanner *sp = new Spanner(*this); + sp->left = c1; + sp->right = c2; + return sp; +} + +Spanner::Spanner() +{ + pstaff_=0; + strets=0; + left = right = 0; +} + +/****************************************************************/ +String +Item::TeXstring() const +{ + return output->TeXstring(); +} + +Interval +Item::width() const +{ + return output->extent().x; +} + +/****************************************************************/ + +Item::Item() +{ + col = 0; + output = 0; + pstaff_ = 0; +} diff --git a/item.hh b/item.hh new file mode 100644 index 0000000000..15a8e88724 --- /dev/null +++ b/item.hh @@ -0,0 +1,47 @@ +#ifndef ITEM_HH +#define ITEM_HH + +#include "glob.hh" +#include "boxes.hh" +#include "string.hh" +#include "tex.hh" + +/// a symbol which is attached between two columns. +struct Spanner { + const PCol *left, *right; + Stretchable_symbol *strets; + PStaff * pstaff_; + /// clone a piece of this spanner. + Spanner *broken_at(const PCol *c1, const PCol *c2) const; + /** + + PRE + c1 >= start, c2 <= stop + */ + String TeXstring () const ; + Spanner(); +}; +/** Spanner should know about the items which it should consider: + e.g. slurs should be steep enough to "enclose" all those items. This + is absolutely necessary for beams, since they have to adjust the + length of stems of notes they encompass. + + */ + +/// a fixed size element of the score +struct Item { + virtual Interval width() const; + + const PCol * col; + Output *output; + + PStaff *pstaff_; + /** needed for knowing at which staff to output this item + */ + String TeXstring () const ; + Item(); +}; +/** An item must be part of a Column +*/ + +#endif diff --git a/keyword.cc b/keyword.cc new file mode 100644 index 0000000000..14903cfb61 --- /dev/null +++ b/keyword.cc @@ -0,0 +1,79 @@ +/* + keyword.cc -- keywords and identifiers + */ + +#include + +#include "glob.hh" +#include "lexer.hh" +//#include "mudobs.hh" +//#include "gram.hh" + +/* for the keyword table */ +struct Keyword_ent +{ + const char *name; + int tokcode; +}; + +struct Keyword_table +{ + Keyword_ent *table; + int maxkey; + Keyword_table(Keyword_ent *); + int lookup(const char *s) const; +}; + + +/* for qsort */ +int + tabcmp(const void * p1, const void * p2) +{ + return strcmp(((const Keyword_ent *) p1)->name, + ((const Keyword_ent *) p2)->name); +} + +Keyword_table::Keyword_table(Keyword_ent *tab) +{ + table = tab; + + /* count keywords */ + for (maxkey = 0; table[maxkey].name; maxkey++); + + /* sort them */ + qsort(table, maxkey, sizeof(Keyword_ent), tabcmp); +} + +/* + lookup with binsearch, return tokencode. +*/ +int +Keyword_table::lookup(const char *s)const +{ + int lo, + hi, + cmp, + result; + lo = 0; + hi = maxkey; + + /* binary search */ + do + { + cmp = (lo + hi) / 2; + + result = strcmp(s, table[cmp].name); + + if (result < 0) + hi = cmp; + else + lo = cmp; + } + while (hi - lo > 1); + if (!strcmp(s, table[lo].name)) + { + return table[lo].tokcode; + } else + return -1; /* not found */ +} + diff --git a/keyword.hh b/keyword.hh new file mode 100644 index 0000000000..aa87a8dc1a --- /dev/null +++ b/keyword.hh @@ -0,0 +1,17 @@ +/* for the keyword table */ +struct Keyword_ent +{ + const char *name; + int tokcode; +}; + +struct Keyword_table +{ + Keyword_ent *table; + int maxkey; + Keyword_table(Keyword_ent *); + int lookup(const char *s) const; +}; + +struct Identifier{ +}; diff --git a/lexer.hh b/lexer.hh new file mode 100644 index 0000000000..28753603ef --- /dev/null +++ b/lexer.hh @@ -0,0 +1,14 @@ +#ifndef LEXER_HH +#define LEXER_HH + +#include "proto.hh" + +void new_input(String s); +bool close_input(); +int yylex(); +void yyerror(char *s); + +int lookup_keyword(String s); +Identifier* lookup_identifier(String s); + +#endif diff --git a/lexer.l b/lexer.l new file mode 100644 index 0000000000..0063227f4e --- /dev/null +++ b/lexer.l @@ -0,0 +1,184 @@ +%{ // -*-Fundamental-*- + +#include +#include +#include "glob.hh" +#include "string.hh" + +#include "lexer.hh" +#include "keyword.hh" +#include "vray.hh" +#include "parser.hh" +#include "debug.hh" + +sstack include_stack; +static int last_print; +const int DOTPRINT=50; // every 50 lines dots +%} + +%option c++ +%option noyywrap +%option nodefault +%option yylineno +%option debug +%x notes +%x incl + +OPTSIGN !? +NOTENAMEI A|B|C|D|E|F|G|As|Bes|Ces|Des|Es|Fes|Ges|Ais|Bis|Cis|Dis|Eis|Fis|Gis +NOTENAMEII a|b|c|d|e|f|g|as|bes|ces|des|es|fes|ges|ais|bis|cis|dis|eis|fis|gis +NOTENAMEIII Ases|Beses|Ceses|Deses|Eses|Feses|Geses|Aisis|Bisis|Cisis|Disis|Eisis|Fisis|Gisis +NOTENAMEIIII ases|beses|ceses|deses|eses|feses|geses|aisis|bisis|cisis|disis|eisis|fisis|gisis +RESTNAME r|s +NOTENAME {NOTENAMEI}|{NOTENAMEII}|{NOTENAMEIII}|{NOTENAMEIIII} +PITCH ['`]*{OPTSIGN}{NOTENAME} +DURNAME 1|2|4|8|16|32 +DURATION {DURNAME}\.* +FULLNOTE {PITCH}{DURATION}? +WORD [a-zA-Z]+ +REAL [0-9]+(\.[0-9]*)? + +%% + +\$ { + BEGIN(notes); return '$'; +} + +{RESTNAME} { + const char *s = YYText(); + yylval.string = new String (s); + if (debug_flags & DEBUGTOKEN) + mtor << "rest:"<< yylval.string; + return RESTNAME; +} +{PITCH} { + const char *s = YYText(); + yylval.string = new String (s); + if (debug_flags & DEBUGTOKEN) + mtor << "pitch:"<< yylval.string; + return PITCH; +} +{DURATION} { + yylval.string = new String (YYText()); + return DURATION; +} +[:space:]+ { +} +[ \t\n]+ { +} +%.* { + +} +\$ { + BEGIN(INITIAL); return '$'; +} +. { + cout << "SCANNER HOLE `" << YYText()<<'\''<> { + if(!close_input()) + yyterminate(); +} +{WORD} { + int l = lookup_keyword(YYText()); + if (l == -1){ + yylval.id = lookup_identifier(YYText()); + return IDENTIFIER; + } else + return l; +} + +{REAL} { + Real r; + sscanf (YYText(), "%lf", &r); + yylval.real = r; + return REAL; +} + +[\{\}\[\]\(\)] { + if (debug_flags & DEBUGTOKEN) + cout << "parens\n"; + return YYText()[0]; +} +[ \t\n]+ { + +} +%.* { + //ignore +} +. { + error("lexer error: illegal character '"+String(YYText()[0])+ + "' encountered"); + return YYText()[0]; +} + +%% + +yyFlexLexer *lexer=0; + +// set the new input to s, remember old file. +void +new_input(String s) +{ + istream *newin ; + + if (s=="") + newin = &cin; + else + newin = new ifstream( s ); // + + if ( ! *newin) + error("cant open " + s); + cout << "["<set_debug( bool(debug_flags & DEBUGPARSER)); + } + + lexer->switch_streams(newin); +} + + +// pop the inputstack. +bool +close_input() +{ + + istream *closing= include_stack.pop(); + if (closing != &cin) + delete closing; + + cout << "]" << flush; + + if (include_stack.empty()) + return false ; + else + lexer->switch_streams(include_stack.top()); + return true; +} + +int +yylex() { + return lexer->yylex(); +} + +void +yyerror(char *s) +{ + *mlog << "error in line " << lexer->lineno() << ": " << s << '\n'; + exit(1); +} + + +#if 0 + +{NOTENAME} { + yylval.string = new String (YYText()); + return NOTENAME; +} + +#endif \ No newline at end of file diff --git a/lilyponddefs.tex b/lilyponddefs.tex new file mode 100644 index 0000000000..48d1e32059 --- /dev/null +++ b/lilyponddefs.tex @@ -0,0 +1,29 @@ + + +\def\musixtwentydefs{ + \font\musicfnt=musix20 +% \hulplijnbreedte5pt + %\hlijnhoogte1pt + %\balkskip4pt +} +\musixtwentydefs +\def\mdef#1#2{\def#1{{\musicfnt\char#2}}} +\mdef\quartball{'007} +\mdef\halfball{'010} +\mdef\wholeball{'011} +\mdef\wholerest{'074} +\mdef\halfrest{'072} +\mdef\quartrest{'076} +\mdef\eighthrest{'077} +\mdef\sixteenthrest{'078} +\mdef\thirtysecondrest{'079} +\mdef\sharp{'065} +\mdef\flat{'063} +\mdef\natural{'067} +\mdef\singledot{'00} +\mdef\doubledot{'01} +\mdef\tripledot{'02} +\def\maatstreep{\vrule height10pt width 1pt} +\parindent0pt + + diff --git a/line.cc b/line.cc new file mode 100644 index 0000000000..9311aeb22d --- /dev/null +++ b/line.cc @@ -0,0 +1,94 @@ +#include "line.hh" +#include "cols.hh" +#include "pscore.hh" + +String +Line_of_staff::TeXstring() const +{ + String s("%line_of_staff\n\\vbox to "); + s += String(height * VERT_TO_PT) +"pt{"; + + //make some room + s += vstrut(base* VERT_TO_PT); + + // the staff itself: eg lines, accolades + s += "\\hbox{"; + { + s+=(*pstaff_->stafsym)(scor->score->linewidth); + PCursor cc(scor->cols); + Real lastpos=cc->hpos; + + // all items in the current line & staff. + for (; cc.ok(); cc++) { + Real delta=cc->hpos - lastpos; + lastpos = cc->hpos; + + // moveover + s +=String( "\\kern ") + HOR_TO_PT*delta + "pt "; + + // now output the items. + + for (PCursor ic(cc->its); ic.ok(); ic++) { + if (ic->pstaff_ == pstaff_) + s += ic->TeXstring(); + } + // spanners. + for (PCursor sc(cc->starters); sc.ok(); sc++) + if (sc->pstaff_ == pstaff_) + s += sc->TeXstring(); + } + } + s+="\\hss}}"; + return s; +} + +String +Line_of_score::TeXstring() const +{ + String s("\\vbox{"); + for (PCursor sc(staffs); sc.ok(); sc++) + s += sc->TeXstring(); + s += "}"; + return s; +} + +/// testing this entry +Line_of_score::Line_of_score(svec sv, + const PScore *ps) +{ + score = ps; + for (int i=0; i< sv.sz(); i++) { + PCol *p=(PCol *) sv[i]; + cols.bottom().add(p); + p->line=this; + } + + for (PCursor sc(score->staffs); sc.ok(); sc++) + staffs.bottom().add(new Line_of_staff(this, sc)); +} +/** construct a line with the named columns. Make the line field + in each column point to this + + #sv# isn't really const!! + */ + +Line_of_staff::Line_of_staff(Line_of_score * sc, PStaff*st) +{ + // [don't know how to calc dimensions yet.] + height = 0.0; + base =0.0; + + scor=sc; + pstaff_=st; + + const PCol *linestart= sc->cols.top(); + const PCol *linestop=sc->cols.bottom(); + + for (PCursor sp(pstaff_->spans); sp.ok(); sp++) { + const PCol *brokenstart = &MAX(*linestart, *sp->left); + const PCol *brokenstop = &MIN(*linestop, *sp->right); + + if (*brokenstop < *brokenstart) + brokenspans.bottom().add(sp->broken_at(brokenstop, brokenstart)); + } +} diff --git a/line.hh b/line.hh new file mode 100644 index 0000000000..cfa3b631a4 --- /dev/null +++ b/line.hh @@ -0,0 +1,52 @@ +#ifndef LINE_HH +#define LINE_HH + +/* + horizontal structures for broken scores. +*/ + +#include "real.hh" +#include "list.hh" +#include "vray.hh" +#include "glob.hh" +#include "pstaff.hh" + + + +/// the columns of a score that form one line. +struct +Line_of_score { + List cols; + + // need to store height of each staff. + PointerList staffs; + const PScore * score; // needed to generate staffs + + /****************/ + + Line_of_score(svec sv, const PScore *); + + String TeXstring() const; + + // is #c# contained in #*this#? + bool element(const PCol *c); +}; + +/// one broken line of staff. +struct Line_of_staff { + Real height; + + /// y-pos of the baseline, measured from the top. + Real base; + + PointerList brokenspans; + Line_of_score const * scor; + const PStaff *pstaff_; + + /****************/ + + String TeXstring() const; + Line_of_staff(Line_of_score*, PStaff *); +}; + +#endif diff --git a/linespace.cc b/linespace.cc new file mode 100644 index 0000000000..7892cefa3d --- /dev/null +++ b/linespace.cc @@ -0,0 +1,249 @@ +#include +#include "linespace.hh" +#include "debug.hh" +#include "qlp.hh" +#include "unionfind.hh" + +const Real COLFUDGE=1e-3; +//#define COLFUDGE 1e-3 +bool +Spacing_problem::contains(const PCol *w) +{ + for (int i=0; i< cols.sz(); i++) + if (cols[i].col == w) + return true; + return false; +} + +int +Spacing_problem::col_id(const PCol *w)const +{ + for (int i=0; i< cols.sz(); i++) + if (cols[i].col == w) + return i; + assert(false); +} + +void +Spacing_problem::OK() const +{ + Union_find connected(cols.sz()); + + for (int i=0; i < ideals.sz(); i++) { + assert(ideals[i]->hooke > 0); + int l = col_id(ideals[i]->left); + int r = col_id(ideals[i]->right); + connected.connect(l,r); + } + + for (int i = 0; i < cols.sz(); i++) { + assert( connected.equiv(0,i)); + } +} + +bool +Spacing_problem::check_constraints(Vector v) const +{ + int dim=v.dim(); + // mtor << "checking solution " << v << '\n'; + for (int i=0; i < dim; i++) { + + if (cols[i].fixed&& ABS(cols[i].fixpos - v(i)) > COLFUDGE) { + return false; + } + if (!i) + continue; + + Real mindist=cols[i-1].minright() + +cols[i].minleft(); + + // ugh... compares + Real dif =v(i) - v(i-1)- mindist; + bool b = (dif > - COLFUDGE); + + +#if 1 + if (!b) + return false; + +#else + mtor << "dif= "<fudge= "<< + b << "\n"; + + /* fucks up for unknown reasons */ + if (dif < -COLFUDGE) + return false; +#endif + + } + return true; +} + +bool +Spacing_problem::check_feasible() const +{ + Vector sol(try_initial_solution()); + return check_constraints(sol); +} + +// generate a solution which obeys the min distances and fixed positions +Vector +Spacing_problem::try_initial_solution() const +{ + int dim=cols.sz(); + Vector initsol(dim); + for (int i=0; i < dim; i++) { + if (cols[i].fixed) { + initsol(i)=cols[i].fixpos; + } else { + Real mindist=cols[i-1].minright() + +cols[i].minleft(); + assert(mindist >= 0.0); + initsol(i)=initsol(i-1)+mindist; + + //nog niet + //if (i>0) + // assert(initsol(i) > initsol(i-1)); + } + } + + return initsol; +} +Vector +Spacing_problem::find_initial_solution() const +{ + Vector v(try_initial_solution()); + assert(check_constraints(v)); + return v; +} +// generate the matrices +void +Spacing_problem::make_matrices(Matrix &quad, Vector &lin) const +{ + quad.fill(0); + lin.fill(0); + for (int j=0; j < ideals.sz(); j++){ + Idealspacing const*i=ideals[j]; + int l = col_id(i->left); + int r = col_id(i->right); + + quad(r,r) += i->hooke; + quad(r,l) -= i->hooke; + quad(l,r) -= i->hooke; + quad(l,l) += i->hooke; + + lin(r) -= i->space*i->hooke; + lin(l) += i->space*i->hooke; + } +} + +// put the constraints into the LP problem +void +Spacing_problem::make_constraints(Optimisation_problem& lp) const +{ + for (int j=0; j < cols.sz(); j++) { + Colinfo *c=&(cols[j]); + int dim=cols.sz(); + + if (c->fixed) { + lp.add_fixed_var(j,c->fixpos); + continue; + }else { + + Vector c1(dim); + Vector c2(dim); + + + c1(j)=1.0 ; + c1(j-1)=-1.0 ; + lp.add_inequality_cons(c1, cols[j-1].minright() + + cols[j].minleft()); + + c2(j)=-1.0 ; + c2(j+1)=1.0; + lp.add_inequality_cons(c2, + cols[j+1].minleft() + + cols[j].minright()); + } + } +} + +svec +Spacing_problem::solve() const +{ + OK(); + assert(check_feasible()); + // print(); + + /* optimalisatiefunctie */ + Optimisation_problem lp(cols.sz()); + make_matrices(lp.quad,lp.lin); + make_constraints(lp); + Vector start=find_initial_solution(); + Vector sol(lp.solve(start)); + if (!check_constraints(sol)) { + error( "solution doesn't solve. Sorry"); + } + + + svec posns(sol); + posns.add(lp.eval(sol)); + return posns; +} + +/* + add one column to the problem. +*/ +void +Spacing_problem::add_column(const PCol *col, bool fixed, Real fixpos) +{ + Colinfo c; + c.fixed=fixed; + c.fixpos=fixpos; + c.col=col; + cols.add(c); +} + +void +Spacing_problem::add_ideal(const Idealspacing *i) +{ + const PCol *l =i->left; + const PCol *r= i->right; + + if (!contains(l) || !contains(r)) { + return; + } + ideals.add(i); +} + +void +Spacing_problem::print_ideal(const Idealspacing*id)const +{ + int l = col_id(id->left); + int r = col_id(id->right); + + mtor << "idealspacing { between " << l <<","<space<< " strength " << id->hooke << "}\n"; +} + +void +Spacing_problem::print() const +{ + for (int i=0; i < cols.sz(); i++) { + mtor << "col " << i<<' '; + cols[i].print(); + } + for (int i=0; i < ideals.sz(); i++) { + print_ideal(ideals[i]); + } +} + +void +Colinfo::print() const +{ + mtor << "column { "; + if (fixed) + mtor << "fixed at " << fixpos<<", "; + mtor << "[" << minleft() << ", " << minright() << "]"; + mtor <<"}\n"; +} diff --git a/linespace.hh b/linespace.hh new file mode 100644 index 0000000000..c2f8b5255f --- /dev/null +++ b/linespace.hh @@ -0,0 +1,98 @@ +#ifndef PROBLEM_HH +#define PROBLEM_HH + +#include "glob.hh" +#include "list.hh" +#include "vray.hh" +#include "cols.hh" +#include "matrix.hh" + +/// helper struct for #Spacing_problem# +struct Colinfo { + const PCol *col; + bool fixed; + Real fixpos; + Colinfo() { + fixed=false; + col=0; + } + void print() const; + Real minright()const { return col->width().max; } + Real minleft()const { return -col->width().min; } +}; + + +/// spacing for one line. +class Spacing_problem { + svec ideals; + svec 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) const; + + /// generate the LP constraints + void make_constraints(Optimisation_problem& lp) const; + +public: + /// solve the spacing problem + svec 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 diff --git a/linestaff.cc b/linestaff.cc new file mode 100644 index 0000000000..38fa40e872 --- /dev/null +++ b/linestaff.cc @@ -0,0 +1,9 @@ +#include "linestaff.hh" +#include "tex.hh" + +Linestaff::Linestaff(int l) +{ + nolines = l; + stafsym = Stretchable_symbol::get_linestaff(l); +} + diff --git a/linestaff.hh b/linestaff.hh new file mode 100644 index 0000000000..7ca34f5739 --- /dev/null +++ b/linestaff.hh @@ -0,0 +1,8 @@ +#include "pstaff.hh" + +struct Linestaff : PStaff { + int nolines; + + Linestaff(int); + +}; diff --git a/main.cc b/main.cc new file mode 100644 index 0000000000..3758840ac6 --- /dev/null +++ b/main.cc @@ -0,0 +1,54 @@ +#include +#include "lgetopt.hh" +#include "misc.hh" +#include "debug.hh" +#include "score.hh" +#include "globvars.hh" + +extern void parse_file(String s); +Score *the_score =0; + +long_option_init theopts[] = { + 1, "debug", 'd', + 1, "output", 'o', + 0,0,0 +}; + + +String outfn="lelie.uit"; + +void +set_output(String s) +{ + outfn = s; +} + +int +main (int argc, char **argv) +{ + Getopt_long oparser(argc, argv,theopts); + + cout << get_version() + << "copyright 1996 Han-Wen Nienhuys\n"; + + while (long_option_init * opt = oparser()) { + switch ( opt->shortname){ + case 'd': + set_debug(oparser.optarg); + break; + case 'o': + set_output(oparser.optarg); + break; + default: + assert(false); + break; + } + } + char *arg = oparser.get_next_arg(); + + if (!arg) arg = ""; + parse_file(arg); + + the_score->process(); + the_score->output(outfn); +} diff --git a/make_version b/make_version new file mode 100755 index 0000000000..5098f79f1d --- /dev/null +++ b/make_version @@ -0,0 +1,8 @@ +#!/bin/sh +echo '#ifndef VERSION_HH' +echo '#define VERSION_HH' +echo '#define MAJORVERSION ' $1 +echo '#define MINORVERSION ' $2 +echo '#define PATCHLEVEL ' $3 +echo '#define VERSIONSTR "'$1.$2.$3'"' +echo '#endif' diff --git a/matrix.cc b/matrix.cc new file mode 100644 index 0000000000..ab59a87308 --- /dev/null +++ b/matrix.cc @@ -0,0 +1,281 @@ +#include "matrix.hh" +#include "string.hh" + + +Real +Matrix::norm() const +{ + Real r =0.0; + for (int i=0, j=0; dat->mult_ok(i,j); dat->mult_next(i,j)) + r += sqr(dat->elem(i,j)); + return sqrt(r); +} + +//inline +Real +Matrix::operator()(int i,int j) const +{ + assert(i >= 0 && j >= 0); + assert(i < rows() && j < cols()); + return dat->elem(i,j); +} + +//inline +Real & +Matrix::operator()(int i, int j) +{ + assert(i >= 0 && j >= 0); + assert(i < rows() && j < cols()); + return dat->elem(i,j); +} + +void +Matrix::fill(Real r) +{ + for (int i=0, j=0; dat->mult_ok(i,j); dat->mult_next(i,j)) + dat->elem(i,j)=r; +} + +void +Matrix::set_diag(Real r) +{ + for (int i=0, j=0; dat->mult_ok(i,j); dat->mult_next(i,j)) + dat->elem(i,j)=(i==j) ? r: 0.0; +} + +void +Matrix::set_diag(Vector d) +{ + for (int i=0, j=0; dat->mult_ok(i,j); dat->mult_next(i,j)) + dat->elem(i,j)=(i==j) ? d(i): 0.0; +} + +void +Matrix::operator+=(const Matrix&m) +{ + assert(m.cols() == cols()); + assert(m.rows() == rows()); + for (int i=0, j=0; dat->mult_ok(i,j); dat->mult_next(i,j)) + dat->elem(i,j) += m(i,j); +} + +void +Matrix::operator-=(const Matrix&m) +{ + assert(m.cols() == cols()); + assert(m.rows() == rows()); + for (int i=0, j=0; dat->mult_ok(i,j); dat->mult_next(i,j)) + dat->elem(i,j) -= m(i,j); +} + + +void +Matrix::operator*=(Real a) +{ + for (int i=0, j=0; dat->mult_ok(i,j); dat->mult_next(i,j)) + dat->elem(i,j) *= a; +} + +void +Matrix::operator=(const Matrix&m) +{ + if (&m == this) + return ; + delete dat; + dat = m.dat->clone(); +} + +Matrix::Matrix(const Matrix &m) +{ + m.OK(); + + dat = m.dat->clone(); +} + + +Matrix::Matrix(int n, int m) +{ + dat = virtual_smat::get_full(n,m); + fill(0); +} + +Matrix::Matrix(int n) +{ + dat = virtual_smat::get_full(n,n); + fill(0); +} + +Matrix::Matrix(Vector v, Vector w) +{ + dat = virtual_smat::get_full(v.dim(), w.dim()); + for (int i=0, j=0; dat->mult_ok(i,j); dat->mult_next(i,j)) + dat->elem(i,j)=v(i)*w(j); +} + + +Vector +Matrix::row(int k) const +{ + int n=cols(); + + + Vector v(n); + for(int i=0; i < n; i++) + v(i)=dat->elem(k,i); + + return v; +} + +Vector +Matrix::col(int k) const +{ + int n=rows(); + Vector v(n); + for(int i=0; i < n; i++) + v(i)=dat->elem(i,k); + return v; +} + +Vector +Matrix::left_multiply(const Vector& v) const +{ + Vector dest(v.dim()); + assert(dat->cols()==v.dim()); + for (int i=0, j=0; dat->mult_ok(i,j); dat->mult_next(i,j)) + dest(i)+= dat->elem(j,i)*v(j); + return dest; +} + +Vector +Matrix::operator *(const Vector& v) const +{ + Vector dest(rows()); + assert(dat->cols()==v.dim()); + for (int i=0, j=0; dat->mult_ok(i,j); dat->mult_next(i,j)) + dest(i)+= dat->elem(i,j)*v(j); + return dest; +} + +Matrix +operator /(Matrix const& m1,Real a) +{ + Matrix m(m1); + m /= a; + return m; +} + +void +Matrix::transpose() // delegate to storage? +{ + for (int i=0, j=0; dat->mult_ok(i,j); dat->mult_next(i,j)) { + if (i >= j) + continue; + Real r=dat->elem(i,j); + dat->elem(i,j) = dat->elem(j,i); + dat->elem(j,i)=r; + } +} + +Matrix +Matrix::operator-() const +{ + OK(); + Matrix m(*this); + m*=-1.0; + return m; +} + +Matrix +Matrix::transposed() const +{ + Matrix m(*this); + m.transpose(); + return m; +} + + +/* should do something smarter: bandmatrix * bandmatrix is also banded matrix. */ +Matrix +operator *(const Matrix &m1, const Matrix &m2) +{ + Matrix result(m1.rows(), m2.cols()); + result.set_product(m1,m2); + return result; +} + +void +Matrix::set_product(const Matrix &m1, const Matrix &m2) +{ + assert(m1.cols()==m2.rows()); + assert(cols()==m2.cols() && rows()==m1.rows()); + + for (int i=0, j=0; dat->mult_ok(i,j); + dat->mult_next(i,j)) { + Real r=0.0; + for (int k = 0; k < m1.cols(); k++) + r += m1(i,k)*m2(k,j); + dat->elem(i,j)=r; + } +} + +void +Matrix::insert_row(Vector v, int k) +{ + assert(v.dim()==cols()); + dat->insert_row(k); + for (int j=0; j < cols(); j++) + dat->elem(k,j)=v(j); +} + + +void +Matrix::swap_columns(int c1, int c2) +{ + assert(c1>=0&& c1 < cols()&&c2 < cols() && c2 >=0); + for (int i=0; i< rows(); i++) { + Real r=dat->elem(i,c1); + dat->elem(i,c1) = dat->elem(i,c2); + dat->elem(i,c2)=r; + } +} + +void +Matrix::swap_rows(int c1, int c2) +{ + assert(c1>=0&& c1 < rows()&&c2 < rows() && c2 >=0); + for (int i=0; i< cols(); i++) { + Real r=dat->elem(c1,i); + dat->elem(c1,i) = dat->elem(c2,i); + dat->elem(c2,i)=r; + } +} + + +int +Matrix::dim() const +{ + assert(cols() == rows()); + return rows(); +} + +Matrix::operator String() const +{ + String s("matrix {\n"); + for (int i=0; i< rows(); i++){ + for (int j = 0; j < cols(); j++) { + s+= String(dat->elem(i,j), "%6f "); + } + s+="\n"; + } + s+="}\n"; + return s; +} + + + +#include "debug.hh" +void +Matrix::print() const +{ + mtor << *this; +} diff --git a/matrix.hh b/matrix.hh new file mode 100644 index 0000000000..e092a5fbe6 --- /dev/null +++ b/matrix.hh @@ -0,0 +1,141 @@ +#ifndef MATRIX_HH +#define MATRIX_HH + + +#include "vsmat.hh" +#include "vector.hh" + +/// a Real matrix +class Matrix { + virtual_smat *dat; + +public: + void OK() const { dat->OK(); } + int cols() const { return dat->cols(); } + int rows() const { return dat->rows(); } + + /// return the size of a matrix + int dim() const; + /** + PRE + the matrix needs to be square. + */ + + // Matrix() { dat = 0; } + ~Matrix() { delete dat; } + + /// set entries to r + void fill(Real r); + + /// set diagonal to d + void set_diag(Real d); + + void set_diag(Vector d); + /// set unit matrix + void unit() { set_diag(1.0); } + + void operator+=(const Matrix&m); + void operator-=(const Matrix&m); + void operator*=(Real a); + void operator/=(Real a) { (*this) *= 1/a; } + + /// add a row + void insert_row(Vector v,int k); + /** + add a row to the matrix before row k + + PRE + v.dim() == cols() + 0 <= k <= rows() + */ + /// + void delete_row(int k) { dat->delete_row(k); } + /** + delete a row from this matrix. + + PRE + 0 <= k < rows(); + */ + void delete_column(int k) { dat->delete_column(k); } + /// + Matrix(int n); + /** + square n matrix, initialised to null + */ + /// + Matrix(int n, int m); + /** + n x m matrix, init to 0 + */ + Matrix(const Matrix &m); + + /// dyadic product: v * w.transpose + Matrix(Vector v, Vector w); + void operator=(const Matrix&m); + + /// access an element + Real operator()(int i,int j) const; + + /// access an element + Real &operator()(int i, int j); + + /// Matrix multiply with vec (from right) + Vector operator *(const Vector &v) const; + + /// set this to m1*m2. + void set_product(const Matrix &m1, const Matrix &m2); + + + Vector left_multiply(Vector const &) const; + + Matrix operator-() const; + + /// transpose this. + void transpose(); + + /// return a transposed copy. + Matrix transposed() const ; + + Real norm() const; + /// swap + void swap_columns(int c1, int c2); + /** + PRE + 0 <= c1,c2 < cols() + */ + + /// swap + void swap_rows(int c1, int c2); + /** + PRE + 0 <= c1,c2 < rows() + */ + + + Vector row(int ) const; + Vector col(int) const; + + operator String() const; + void print() const; +}; + +/** This is a class for a nonsquare block of #Real#s. The + implementation of sparse matrices is done in the appropriate #smat# + class. Matrix only does the mathematical actions (adding, + multiplying, etc.) + + + TODO + implement ref counting? */ + + +inline Vector +operator *(Vector &v, const Matrix& m) { return m.left_multiply(v); } +Matrix operator *(const Matrix& m1,const Matrix &m2); +Matrix operator /(const Matrix &m1,Real a); +inline Matrix operator -(Matrix m1,const Matrix m2) +{ + m1 -= m2; + return m1; +} +#endif diff --git a/misc.cc b/misc.cc new file mode 100644 index 0000000000..d07c358d1a --- /dev/null +++ b/misc.cc @@ -0,0 +1,32 @@ +#include "misc.hh" +#include "glob.hh" +#include "mtime.hh" +#include + +int intlog2(int d) { + int i=0; + while (!(d&1)) { + d/= 2; i++; + } + assert(!(d/2)); + return i; +} + +double log2(double x) { + return log(x) /log(2.0); +} + + +// golden ratio + +const Real PHI = (1+sqrt(5))/2; +const double ENGRAVERS_SPACE = PHI; +const double WHOLE_SPACE = 10.0; + + +Real +duration_to_idealspace(Mtime d) +{ + // see Roelofs, p. 57 + return WHOLE_SPACE * pow(ENGRAVERS_SPACE, log2(d)); +} diff --git a/misc.hh b/misc.hh new file mode 100644 index 0000000000..a0ce0161ad --- /dev/null +++ b/misc.hh @@ -0,0 +1,8 @@ +#ifndef MISC_HH +#define MISC_HH +#include "mtime.hh" +int intlog2(int d); +Real duration_to_idealspace(Mtime d); +class String; +char *get_version(); +#endif diff --git a/molecule.cc b/molecule.cc new file mode 100644 index 0000000000..96a21b9025 --- /dev/null +++ b/molecule.cc @@ -0,0 +1,87 @@ +#include "glob.hh" +#include "string.hh" +#include "molecule.hh" + +String +Atom::TeXstring() const +{ + // whugh.. Hard coded... + String s("\\raise"); + s+= String(off.y * VERT_TO_PT)+"pt\\hbox to 0pt{\\kern "; + s+=String(off.x * HOR_TO_PT) + "pt" + sym->tex + "\\hss}"; + return s; +} + +/****************************************************************/ + +String +Molecule::TeXstring() const +{ + String s; + for(Cursor c(ats); c.ok(); c++) + s+=(*c).TeXstring(); + return s; +} + +Box +Molecule::extent() const +{ + Box b; + for(Cursor c(ats); c.ok(); c++) + b.unite((*c).extent()); + return b; +} + +void +Molecule::translate(Offset o) +{ + for(Cursor c(ats); c.ok(); c++) + (*c).translate(o); +} + +void +Molecule::add(const Molecule &m) +{ + for (Cursor c(m.ats); c.ok(); c++) { + Atom a(c); + ats.bottom().add(a); + } +} + +void +Molecule::add_right(const Molecule &m) +{ + Real xof=extent().x.max - m.extent().x.min; + Molecule toadd(m); + toadd.translate(Offset(xof, 0.0)); + add(toadd); +} + +void +Molecule::add_left(const Molecule &m) +{ + Real xof=extent().x.min - m.extent().x.max; + Molecule toadd(m); + toadd.translate(Offset(xof, 0.0)); + add(toadd); +} + + +void +Molecule::add_top(const Molecule &m) +{ + Real yof=extent().y.max - m.extent().y.min; + Molecule toadd(m); + toadd.translate(Offset(0,yof)); + add(toadd); +} + +void +Molecule::add_bot(const Molecule &m) +{ + Real yof=extent().y.min- m.extent().y.max; + Molecule toadd(m); + toadd.translate(Offset(0,yof)); + add(toadd); +} + diff --git a/molecule.hh b/molecule.hh new file mode 100644 index 0000000000..c98cfe5f49 --- /dev/null +++ b/molecule.hh @@ -0,0 +1,50 @@ +#ifndef MOLECULE_HH +#define MOLECULE_HH + +#include "list.hh" +#include "boxes.hh" +#include "item.hh" + +/// a symbol which can be translated, and freely copied +struct Atom { + Offset off; + const Symbol * sym; + + void translate(Offset o) { + off += o; + } + + /// how big is #this#? + Box extent() const { + Box b( sym->dim); + b.translate(off); + return b; + } + Atom(const Symbol*s) { + sym=s; + } + String TeXstring() const; +}; + +/// a group of #Atom#s +struct Molecule : Output { + List ats; + + Molecule() { } + Molecule(Atom a) { ats.bottom().add(a); } + // Molecule(Molecule const&src); + void add_right(const Molecule &m); + void add_left(const Molecule &m); + void add_top(const Molecule &m); + void add_bot(const Molecule &m); + void add(Molecule const &m); + void translate(Offset); + + /// how big is #this#? + Box extent() const; + + String TeXstring() const; +}; +/** a group of individually translated symbols. You can add molecules + to the top, to the right, etc. */ +#endif diff --git a/mtime.hh b/mtime.hh new file mode 100644 index 0000000000..f42c87b82d --- /dev/null +++ b/mtime.hh @@ -0,0 +1,8 @@ +#ifndef MTIME_HH +#define MTIME_HH + +#include "real.hh" + +typedef Real Mtime; + +#endif MTIME_HH diff --git a/note.cc b/note.cc new file mode 100644 index 0000000000..538f26a28e --- /dev/null +++ b/note.cc @@ -0,0 +1,130 @@ +#include +#include "string.hh" +#include "real.hh" +#include "debug.hh" +#include "request.hh" +#include "voice.hh" +#include "notename.hh" + +int default_duration = 4; + +void +parse_duration(const char *a, int &j, int &intdur, int &dots) +{ + String durstr; + while (isdigit(a[j])) + { + durstr += a[j++]; + } + + dots=0; + + while (a[j] == '.') + { + j++; + dots++; + } + intdur = (durstr.len()) ? + durstr.value():default_duration; + + if (debug_flags & DEBUGTOKEN) + mtor << "dur " << intdur << "dots " << dots<octave = oct; + rq->accidental = acc; + rq->forceacc = forceacc; + rq->balltype = dur; + rq->dots = dots; + + + v->add(rq); + return v; +} + +Voice_element * +get_rest_element(String type, 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(v); + + rq->balltype = dur; + rq->dots = dots; + + v->add(rq); + return v; +} diff --git a/notename.cc b/notename.cc new file mode 100644 index 0000000000..8ebc4df244 --- /dev/null +++ b/notename.cc @@ -0,0 +1,30 @@ +#include "glob.hh" +#include "string.hh" + + +/// change this along with lex file for other notenames. +const char *notetab[] = +{ +"ceses", "ces", "c", "cis", "cisis", +"deses", "des", "d", "dis", "disis", +"eses", "es", "e", "eis", "eisis", +"feses", "fes", "f", "fis", "fisis", +"geses", "ges", "g", "gis", "gisis", +"ases", "as", "a", "ais", "aisis", +"beses", "bes", "b", "bis", "bisis", +0 +}; + +void +lookup_notename(int &large, int &small, String s) +{ + int i; + for (i =0; notetab[i]; i++) + if (s == notetab[i]) + { + large = i /5; + small = i %5 - 2; + return; + } + assert(false); +} diff --git a/notename.hh b/notename.hh new file mode 100644 index 0000000000..ef04a0da7c --- /dev/null +++ b/notename.hh @@ -0,0 +1,2 @@ + +void lookup_notename(int &large, int &small, String s); diff --git a/parseconstruct.hh b/parseconstruct.hh new file mode 100644 index 0000000000..820499cb36 --- /dev/null +++ b/parseconstruct.hh @@ -0,0 +1,7 @@ + +#include "proto.hh" + +Staff * get_new_rhythmstaff(); +Voice_element * get_note_element(String,String); +Voice_element* get_rest_element(String,String); +Command * get_bar_command(Real); diff --git a/parser.y b/parser.y new file mode 100644 index 0000000000..23987a273d --- /dev/null +++ b/parser.y @@ -0,0 +1,121 @@ +%{ // -*-Fundamental-*- +#include +//#include "mudobs.hh" +#include "lexer.hh" +#include "staff.hh" +#include "score.hh" +#include "keyword.hh" +#include "globvars.hh" +#include "debug.hh" +#include "parseconstruct.hh" +#define YYDEBUG 1 + + +%} + + +%union { + int i; + Real real; + Command *command; + Identifier *id; + + Voice *voice; + Voice_element *el; + Staff *staff; + String *string; + Score *score; +} + +%token VOICE STAFF SCORE TITLE RHYTHMSTAFF BAR NOTENAME + +%token IDENTIFIER +%token PITCH DURATION RESTNAME +%token REAL + + +%type voice_block voice_body voice_elts voice_elts_dollar +%type voice_elt +%type score_command +%type score_block score_body +%type staff_block rhythmstaff_block rhythmstaff_body + +%% + +mudela: + score_block { + delete the_score; + the_score = $1; + } + ; + + +score_block: SCORE '{' score_body '}' { $$ = $3; } + ; + +score_body: { $$ = new Score; } + | score_body staff_block { $$->add($2); } + | score_body score_command { $$->add($2); } + ; + +staff_block: + rhythmstaff_block + ; + +rhythmstaff_block: + RHYTHMSTAFF '{' rhythmstaff_body '}' { $$ = $3; } + ; + +rhythmstaff_body: + /* empty */ { $$ = get_new_rhythmstaff(); } + | rhythmstaff_body voice_block { $$->add_voice($2); } + ; + +voice_block: + VOICE '{' voice_body '}' { $$ = $3; } + ; + + +voice_body: + REAL voice_elts_dollar { $$ = $2; $$->start = $1; } + | voice_elts_dollar { $$ = $1; } + ; + +voice_elts_dollar: + '$' voice_elts '$' { $$ = $2; } + ; + +voice_elts: + /* empty */ { + $$ = new Voice; + } + | voice_elts voice_elt { + $$->add($2); + } + ; + +voice_elt: + PITCH DURATION { $$ = get_note_element(*$1, *$2); + + } + | RESTNAME DURATION { $$ = get_rest_element(*$1, *$2); + + } + ; + +score_command: + BAR REAL { + $$ = get_bar_command($2); + } + ; + +%% + +void +parse_file(String s) +{ + *mlog << "Parsing ... "; + yydebug = debug_flags & DEBUGPARSER; + new_input(s); + yyparse(); +} diff --git a/proto.hh b/proto.hh new file mode 100644 index 0000000000..0721e19fd4 --- /dev/null +++ b/proto.hh @@ -0,0 +1,76 @@ +#include "real.hh" + +class Vector; +class Matrix; +class Line_of_score; +class Line_of_staff; +class PCol; +class PStaff; +class Staff; +class Staff_column; +class Score; +class Score_column; +class Voice; +class Voice_element; +class Voicegroup; +class Request; +class Command; +class Request; +class Stem_req; +class Span_req; +class Slur_req; +class Decresc_req; +class Cresc_req; +class Bracket_req; +class Script_req; +class Rest_req; +class Note_req; +class Lyric_req; +class Chord; +class Absdynamic_req; +struct Offset; +struct Interval; +struct Box; +struct PCol; +struct Idealspacing; +struct Spanner; +struct Item; +struct Line_of_staff; +struct Colinfo; +struct Linestaff; +struct Atom; +struct Molecule; +struct PScore; +struct Request; +struct Note_req; +struct Lyric_req; +struct Script_req; +struct Rest_req; +struct Chord; +struct Stem_req; +struct Span_req; +struct Beam_req; +struct Bracket_req; +struct Slur_req; +struct Dynamic; +struct Cresc_req; +struct Decresc_req; +struct Absdynamic_req; +struct Score_column; +struct Score; +struct Staff_column; +struct Staff; +struct Command; +struct Symbol; +struct Stretchable_symbol; +struct Output; +struct Text_gob; +struct Voice; +struct Voicegroup; +struct Voice_element; +struct String; +struct Tex_stream; +struct Identifier; +struct Keyword; +class Mixed_qp; +typedef Mixed_qp Optimisation_problem; diff --git a/pscore.cc b/pscore.cc new file mode 100644 index 0000000000..dd8ae697bc --- /dev/null +++ b/pscore.cc @@ -0,0 +1,99 @@ +// utility functions for PScore +#include "debug.hh" +#include "line.hh" +#include "pscore.hh" +#include "tstream.hh" + +void +PScore::clean_cols() +{ + for (PCursor c(cols); c.ok(); c++) + if (!c->used) { + c.remove(); + //mtor << "removing pcol\n"; + } +} + + +void +PScore::add(PStaff *s) +{ + staffs.bottom().add(s); +} + +void +PScore::typeset_item(Item *i, PCol *c, PStaff *s, int breakstat) +{ + assert(c && i && s); + if (breakstat == 1 ) { + typeset_item(i, c->prebreak, s, 0); + } if (breakstat == 3) + typeset_item(i, c->prebreak, s, 0 ); + else{ + its.bottom().add(i); + s->add(i); + c->add(i); + } +} + +void +PScore::add_line(svec curline, svec config) +{ + Line_of_score *p = new Line_of_score(curline,this); + lines.bottom().add(p); + for (int i=0; i < curline.sz(); i++){ + PCol *c=(PCol *)curline[i]; // so, this isn't really const. + c->hpos= config[i]; + } +} + +Idealspacing* +PScore::get_spacing(PCol*l, PCol*r) +{ + assert(l!=r); + for (PCursor ic (suz); ic.ok(); ic++) { + if (ic->left == l && ic->right == r){ + return ic; + } + } + + Idealspacing*ip =new Idealspacing(l,r); + suz.bottom().add(ip); + // l->used = r->used = true; + return ip; +} + +svec +PScore::find_breaks() const +{ + svec retval; + for (PCursor c(cols); c.ok(); c++) + if (c->breakable) + retval.add(c); + + return retval; +} + +void +PScore::add(PCol *p) +{ + cols.bottom().add(p); +} +/* + todo: config of width + */ +PScore::PScore() +{ + linewidth = 15; // in cm for now +} + +void +PScore::output(Tex_stream &ts) +{ + int l=1; + ts << "% linewidth " << linewidth * HOR_TO_PT << " pt\n"; + for (PCursor lic(lines); lic.ok(); lic++) { + ts << "% line of score no. " << l++ <<"\n"; + ts << lic->TeXstring(); + } +} diff --git a/pscore.hh b/pscore.hh new file mode 100644 index 0000000000..75da46a4a5 --- /dev/null +++ b/pscore.hh @@ -0,0 +1,80 @@ +// the breaking problem for a score. + +#ifndef PSCORE_HH +#define PSCORE_HH + + +#include "vray.hh" +#include "cols.hh" +#include "pstaff.hh" + +/// all stuff which goes onto paper +struct PScore { + /// width of paper + Real linewidth; + + /// the columns, ordered left to right + PointerList cols; + + /// the idealspacings, no particular order + PointerList suz; + + /// the staffs ordered top to bottom + PointerList staffs; + + /// all symbols in score. No particular order. + PointerList its; + + /// if broken, the different lines + PointerList lines; + + /// crescs etc; no particular order + PointerList spanners; + + /****************************************************************/ + + void calc_breaking(); + /** + calculate where the lines are to be broken. + + POST + + lines contain the broken lines. + */ + + /// search all pcols which are breakable. + svec find_breaks() const; + + /// add a line to the broken stuff. Positions given in #config# + void add_line(svec curline, svec config); + + /// helper: solve for the columns in #curline#. + svec solve_line(svec curline) const; + + void add(PStaff *); + /// add item + void typeset_item(Item *, PCol *,PStaff*,int); + /// 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. + */ + + + PCursor find_col(PCol *); + void clean_cols(); + void problem_OK() ; + + PScore(); +}; +/** 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 diff --git a/pstaff.cc b/pstaff.cc new file mode 100644 index 0000000000..c922fe9996 --- /dev/null +++ b/pstaff.cc @@ -0,0 +1,13 @@ +#include "pstaff.hh" + +PStaff::PStaff() +{ + stafsym = 0; +} + +void +PStaff::add(Item *i ) +{ + its.bottom().add(i); + i->pstaff_ = this; +} diff --git a/pstaff.hh b/pstaff.hh new file mode 100644 index 0000000000..de535a6518 --- /dev/null +++ b/pstaff.hh @@ -0,0 +1,19 @@ +#ifndef PSTAFF_HH +#define PSTAFF_HH + +#include "list.hh" +#include "item.hh" + +/// items grouped vertically. +class PStaff { +public: + Stretchable_symbol *stafsym; + List spans; + List its; + + void add(Item*i); + PStaff(); + virtual ~PStaff() {} +}; + +#endif diff --git a/qlp.cc b/qlp.cc new file mode 100644 index 0000000000..21997f6364 --- /dev/null +++ b/qlp.cc @@ -0,0 +1,137 @@ +#include "debug.hh" +#include "qlp.hh" +#include "choleski.hh" +void +Mixed_qp::add_equality_cons(Vector v, double r) +{ + assert(false); +} +void +Mixed_qp::add_fixed_var(int i, Real r) +{ + + eq_cons.add(i); + eq_consrhs.add(r); +} + void +Ineq_constrained_qp::add_inequality_cons(Vector c, double r) +{ + cons.add(c); + consrhs.add(r); +} + +Ineq_constrained_qp::Ineq_constrained_qp(int novars): + quad(novars), + lin(novars) +{ +} + +void +Ineq_constrained_qp::OK()const +{ + assert(cons.sz() == consrhs.sz()); + Matrix Qdif= quad - quad.transposed(); + assert(Qdif.norm() < EPS); +} + + +Real +Ineq_constrained_qp::eval (Vector v) +{ + return v * quad * v + lin * v ; +} +/* + eliminate appropriate variables, until we have a Ineq_constrained_qp + then solve that. + + PRE + cons should be ascending + */ +Vector +Mixed_qp::solve(Vector start) const +{ + + Ineq_constrained_qp pure(*this); + + for (int i= eq_cons.sz()-1; i>=0; i--) { + pure.eliminate_var(eq_cons[i], eq_consrhs[i]); + start.del(eq_cons[i]); + } + Vector sol = pure.solve(start); + for (int i= 0; i < eq_cons.sz(); i++) { + sol.insert( eq_consrhs[i],eq_cons[i]); + } + return sol; +} + +/* + assume x(idx) == value, and adjust constraints, lin and quad accordingly + */ +void +Ineq_constrained_qp::eliminate_var(int idx, Real value) +{ + Vector row(quad.row(idx)); + row*= value; + + quad.delete_row(idx); + + quad.delete_column(idx); + + lin.del(idx); + row.del(idx); + lin +=row ; + + for (int i=0; i < cons.sz(); i++) { + consrhs[i] -= cons[i](idx) *value; + cons[i].del(idx); + } +} + + + + +Mixed_qp::Mixed_qp(int n) + : Ineq_constrained_qp(n) +{ +} + +void +Mixed_qp::OK()const +{ + Ineq_constrained_qp::OK(); + assert(eq_consrhs.sz() == eq_cons.sz()); +} +void +Ineq_constrained_qp::print() const +{ + + mtor << "Quad " << quad; + mtor << "lin " << lin <<"\n"; + for (int i=0; i < cons.sz(); i++) { + mtor << "constraint["<= " << consrhs[i]; + mtor << "\n"; + } +} +void +Mixed_qp::print() const +{ + Ineq_constrained_qp::print(); + for (int i=0; i < eq_cons.sz(); i++) { + mtor << "eq cons "< binding; + for (int i=0; i < cons.sz(); i++) { + Real R=cons[i] * sol- consrhs[i]; + assert(R> -EPS); + if (R < EPS) + binding.add(i); + } + // KKT check... + // todo +} diff --git a/qlp.hh b/qlp.hh new file mode 100644 index 0000000000..83938c9ca7 --- /dev/null +++ b/qlp.hh @@ -0,0 +1,89 @@ +#ifndef QLP_HH +#define QLP_HH + +#include "matrix.hh" + +/// inequality constrained quadratic program +class Ineq_constrained_qp { + friend class Active_constraints; + + svec cons; + svec consrhs; +public: + Matrix quad; + Vector lin; + + + /// + void assert_solution(Vector sol) const; + /** + use a KKT method to assert optimality of sol + */ + /// solve the problem using a variable metric method + Vector solve(Vector start) const; + + int dim() const{ + return lin.dim(); + } + /** return the number of variables in the problem */ + /// + void add_inequality_cons(Vector c, double r); + /** + add a constraint + + + c*vars >= r + + PRE + c.dim() == dim(); + + */ + /// + Ineq_constrained_qp(int novars); + /** set up matrices to go with the problem. */ + + Real eval(Vector v); + /** + evaluate the quadratic function for input #v# + */ + + void eliminate_var(int idx, Real value); + void OK()const; + void print() const; + +}; + +/// Quadratic programming with mixed linear constraints +class Mixed_qp :public Ineq_constrained_qp { + svec eq_cons; + svec eq_consrhs; +public: + Mixed_qp(int n); + void OK() const; + void print() const; + + Vector solve(Vector start) const; + void add_fixed_var(int i , Real value); + + /// + void add_equality_cons(Vector c, double r); + /** + add a constraint, + + c*vars == r + + PRE + c.dim()==dim(); + */ + +}; +/** + problem definition of a quadratic optimisation problem with linear + inequality and equality constraints + + + x^T QUAD x /2 + b^T x +*/ + +typedef Mixed_qp Optimisation_problem; +#endif diff --git a/qlpsolve.cc b/qlpsolve.cc new file mode 100644 index 0000000000..0884223e6c --- /dev/null +++ b/qlpsolve.cc @@ -0,0 +1,230 @@ +#include "qlpsolve.hh" +#include "debug.hh" +#include "choleski.hh" + +const Real TOL=1e-2; // roughly 1/10 mm + +String +Active_constraints::status() const +{ + String s("Active|Inactive ["); + for (int i=0; i< active.sz(); i++) { + s += String(active[i]) + " "; + } + + s+="|"; + for (int i=0; i< inactive.sz(); i++) { + s += String(inactive[i]) + " "; + } + s+="]"; + + return s; +} + +void +Active_constraints::OK() { + H.OK(); + A.OK(); + assert(active.sz() +inactive.sz() == opt->cons.sz()); + assert(H.dim() == opt->dim()); + assert(active.sz() == A.rows()); + svec allcons; + + for (int i=0; i < opt->cons.sz(); i++) + allcons.add(0); + for (int i=0; i < active.sz(); i++) { + int j = active[i]; + allcons[j]++; + } + for (int i=0; i < inactive.sz(); i++) { + int j = inactive[i]; + allcons[j]++; + } + for (int i=0; i < allcons.sz(); i++) + assert(allcons[i] == 1); +} + +Vector +Active_constraints::get_lagrange(Vector gradient) +{ + Vector l(A*gradient); + + return l; +} + +void +Active_constraints::add(int k) +{ + // add indices + int cidx=inactive[k]; + active.add(cidx); + + inactive.swap(k,inactive.sz()-1); + inactive.pop(); + + Vector a( opt->cons[cidx] ); + // update of matrices + Vector Ha = H*a; + Real aHa = a*Ha; + + H -= Matrix(Ha , Ha)/(aHa); + + Vector addrow(Ha/(aHa)); + A -= Matrix(A*a, addrow); + A.insert_row(addrow,A.rows()); +} + +void +Active_constraints::drop(int k) +{ + int q=active.sz()-1; + + // drop indices + inactive.add(active[k]); + active.swap(k,q); + A.swap_rows(k,q); + active.pop(); + + Vector a(A.row(q)); + H += Matrix(a,a)/(a*opt->quad*a); + A -= A*opt->quad*Matrix(a,a)/(a*opt->quad*a); + + Vector rem_row(A.row(q)); + assert(rem_row.norm() < EPS); + A.delete_row(q); +} + + +Active_constraints::Active_constraints(Ineq_constrained_qp const *op) + : A(0,op->dim()), + H(op->dim()), + opt(op) +{ + for (int i=0; i < op->cons.sz(); i++) + inactive.add(i); + Choleski_decomposition chol(op->quad); + H=chol.inverse(); +} + +/* Find the optimum which is in the planes generated by the active + constraints. + */ +Vector +Active_constraints::find_active_optimum(Vector g) +{ + return H*g; +} + +/****************************************************************/ + +int +min_elt_index(Vector v) +{ + Real m=INFTY; int idx=-1; + for (int i = 0; i < v.dim(); i++) + if (v(i) < m) { + idx = i; + m = v(i); + } + return idx; +} + +///the numerical solving +Vector +Ineq_constrained_qp::solve(Vector start) const +{ + Active_constraints act(this); + + + act.OK(); + + + Vector x(start); + Vector gradient=quad*x+lin; + + + Vector last_gradient(gradient); + int iterations=0; + + while (iterations++ < MAXITER) { + Vector direction= - act.find_active_optimum(gradient); + + // mtor << "gradient "<< gradient<< "\ndirection " << direction<<"\n"; + + if (direction.norm() > EPS) { + // mtor << act.status() << '\n'; + + Real minalf = INFTY; + + Inactive_iter minidx(act); + + + /* + we know the optimum on this "hyperplane". Check if we + bump into the edges of the simplex + */ + + for (Inactive_iter ia(act); ia.ok(); ia++) { + + if (ia.vec() * direction >= 0) + continue; + Real alfa= - (ia.vec()*x - ia.rhs())/ + (ia.vec()*direction); + + if (minalf > alfa) { + minidx = ia; + minalf = alfa; + } + } + Real unbounded_alfa = 1.0; + Real optimal_step = MIN(minalf, unbounded_alfa); + + Vector deltax=direction * optimal_step; + x += deltax; + gradient += optimal_step * (quad * deltax); + + //mtor << "step = " << optimal_step<< " (|dx| = " << + //deltax.norm() << ")\n"; + + if (minalf < unbounded_alfa) { + /* bumped into an edge. try again, in smaller space. */ + act.add(minidx.idx()); + continue; + } + /*ASSERT: we are at optimal solution for this "plane"*/ + + + } + + Vector lagrange_mult=act.get_lagrange(gradient); + int m= min_elt_index(lagrange_mult); + + if (m>=0 && lagrange_mult(m) > 0) { + break; // optimal sol. + } else if (m<0 && gradient.norm() < EPS) { + break; + } + + mtor << "dropping cons " << m<<'\n'; + act.drop(m); + } + if (iterations >= MAXITER) + WARN<<"didn't converge!\n"; + + // mtor << ": found " << x<<" in " << iterations <<" iterations\n"; + assert_solution(x); + return x; +} + +/** Mordecai Avriel, Nonlinear Programming: analysis and methods (1976) + Prentice Hall. + + Section 13.3 + + This is a "projected gradient" algorithm. Starting + from a point x the next point is found in a direction determined by + projecting the gradient onto the active constraints. */ + +/* + thoroughly hacked to barely living tiny pieces by HW + */ diff --git a/qlpsolve.hh b/qlpsolve.hh new file mode 100644 index 0000000000..4d29bcc4a0 --- /dev/null +++ b/qlpsolve.hh @@ -0,0 +1,78 @@ +#include "qlp.hh" +#include "matrix.hh" + + +class Active_constraints { + friend class Inactive_iter; + + + Matrix A,H; + svec active; + svec inactive; // actually this is a set, not an array. + const Ineq_constrained_qp *opt; + +public: + String status()const; + + Vector vec(int k) const { return opt->cons[k]; } + Real rhs(int k) const { return opt->consrhs[k]; } + + /// drop constraint + void drop (int k); + /** drop constraint k from the active set. k is the index of the + constraint in #active# + + */ + + /// add constraint j + void add(int j); + /** + add constraint j to the active set j is the index of the + constraint in #inactive# + */ + + /// exchange in and out. + void exchange(int in, int out) { add(in); drop (out); } + + /// + Vector find_active_optimum(Vector g); + + /// get lagrange multipliers. + Vector get_lagrange(Vector v); + + Active_constraints(Ineq_constrained_qp const *op); + /** construct: no constraints active, n vars. Put the equalities + into the constraints. */ + + /// check invariants + void OK(); +}; + +/** + This class represents the set of active (binding) constraints + which can be active while the QLP algorithm is in a feasible + point. The active constraints are numbered. + If the constraints are of the form + + A^T*x >= b + + then the binding constraints are those where the >= is equality. + + */ + +/// +class Inactive_iter { + int j; + Active_constraints const* ac; +public: + Inactive_iter(Active_constraints const &c) { ac=&c; j=0; } + int idx() const { return j; } + void operator ++(int) { j++; } + int constraint_id() const { return ac->inactive[j]; } + Vector vec() const { return ac->vec(constraint_id()); } + Real rhs() const { return ac->rhs(constraint_id()); } + bool ok() const { return j < ac->inactive.sz(); } +}; +/** + loop through the inactive constraints. + */ diff --git a/real.hh b/real.hh new file mode 100644 index 0000000000..1f2187c8c1 --- /dev/null +++ b/real.hh @@ -0,0 +1,25 @@ +#ifndef REAL_HH +#define REAL_HH +typedef double Real; +inline Real sqr(Real x){ + return x*x; +} +inline Real MIN(Real x, Real y) { + return (x < y)? x : y; +} + +inline Real MAX(Real x, Real y) { + return (x > y) ? x : y; +} + +inline Real ABS(Real x) +{ + return (x>0)? x:-x; +} +inline +int sgn(Real x) { + if (!x)return 0; + return (x > 0) ?1: -1; +} + +#endif diff --git a/request.cc b/request.cc new file mode 100644 index 0000000000..27029ba8d7 --- /dev/null +++ b/request.cc @@ -0,0 +1,70 @@ +#include "request.hh" +Request::Request(Voice_element*v) +{ + elt = v; + tag = UNKNOWN; +} + +Note_req::Note_req(Voice_element*v): + Request(v) +{ + name = 'c'; + octave = 0; + accidental = 0; + forceacc = false; + balltype = 1; + dots = 0; + tag = NOTE; +} + +Rest_req::Rest_req(Voice_element*v) + :Request(v) +{ + balltype = 1; + dots = 0; + tag =REST; +} + +Request::Request() +{ + elt = 0; + tag = UNKNOWN; +} + +Note_req* +Request::note() +{ + assert(tag == NOTE); + return (Note_req*)this; +} + +Rest_req* +Request::rest() +{ + assert(tag == REST); + return (Rest_req*)this; +} + + +Real +wholes(int dur, int dots) +{ + Real f = 1.0/Real(dur); + Real delta = f; + + while (dots--) { + delta /= 2.0; + f += delta; + } + return f; +} + +Real +Note_req::duration() const { + return wholes( balltype,dots); +} +Real +Rest_req::duration() const{ + return wholes( balltype,dots); +} + diff --git a/request.hh b/request.hh new file mode 100644 index 0000000000..0cd0fe0903 --- /dev/null +++ b/request.hh @@ -0,0 +1,195 @@ +// mpp96's second egg of columbus! +#ifndef REQUEST_HH +#define REQUEST_HH + +#include "glob.hh" +#include "string.hh" +#include "mtime.hh" +struct Request { + Voice_element*elt; + enum { + UNKNOWN, NOTE, REST, LYRIC, SCRIPT, CHORD, BEAM, + BRACKET, STEM, SLUR, CRESC, DECRESC, ABSDYNAMIC + } tag; + + Note_req *note(); + Rest_req *rest(); + Request(Voice_element*); + Request(); + virtual Real duration() const { return 0.0; } +}; + +/** + Any Voice_element can do a number of requests. A request is done + to the #Staff# which contains the #Voice_element#. The staff decides + whether to to honor the request, ignore it, or merge it with other + requests. Merging of requests is preferably done with other + requests done by members of the same voicegroups (beams, brackets, stems) + + Please refer to the documentation of the Child classes of + #Request# for explanation of each request type. + + The result of a request will be an #Item# or a #Spanner#, which + will be put on a #PStaff#. Note that the #PStaff# and the original + #Staff# need not have anything in common. For example, the + ``double'' piano Staff could interpret commands which juggle + melodies across the left and right hand, and may put the result in + two five-line PStaffs (maybe with extra PStaffs to carry the dynamic + signs and any lyric. + + The class #Staff# should be thought as a container for the + #Voice#s, and an interpreter for #Request#s and #Command#s. + Different staffs can produce different outputs; a melodious voice + which is put into a percussion-Staff, will be typeset as the rythm of + that voice. + + After #Staff# made up her mind (Would #Staff# be a smart + name? How about #struct Lily {}# :-), the resultant items and + spanners are put on the PScore, and pointers to these items are + stored in the #Voice_element#. This construction enables the + beams/stems to look up the balls it has to connect to. */ + + + +/// Put a note of specified type, height, and with accidental on the staff. +struct Note_req : Request { + char name; + int octave; + int accidental; + bool forceacc; + int balltype; + int dots; + + Real duration() const; + Note_req(Voice_element*v); +}; +/** +Staff has to decide if the ball should be hanging left or right. This +influences the horizontal dimensions of a column, and this is why +request processing should be done before horizontal spacing. + +Other voices' frivolities may cause the need for accidentals, so this +is also for the Staff to decide. The Staff can decide on positioning +based on ottava commands and the appropriate clef. +*/ + +///Put a lyric above or below (?) this staff. +struct Lyric_req : Request { + String text; +}; + + +///Put a script above or below this ``note'' +struct Script_req : Request { + int orientation; + Symbol *sym; +}; +/** +eg upbow, downbow. Why a request? These symbols may conflict with slurs and brackets, so this +also a request +*/ + + +///Put a rest on the staff. +struct Rest_req : Request { + int balltype; + int dots; + Rest_req(Voice_element*); + Real duration() const; +}; +/** +Why a request? It might be a good idea to not typeset the rest, if the paper is too crowded. +*/ + + + +///Draw a (Guitar) chord above or below this ``note'' +struct Chord : Request { + // don't know how this looks. +}; +/** +Why a request? +Because everything else is done in requests. +*/ + + +/// for absolute dynamics +enum Loudness { + FFF, FF, F, MF, MP, P, PP, PPP +} ; + +/// attach a stem to the noteball +struct Stem_req : Request { + /// 4,8,16, .. + int stem_number ; +}; +/// requests to start or stop something. +struct Span_req : Request { + /// should the spanner start or stop, or is it unwanted? + enum { + NOSPAN, START, STOP + } spantype ; +}; +/** + This type of request typically results in the creation of a #Spanner# +*/ + + +///Start / stop a beam at this note. +struct Beam_req : Span_req { + int nplet; +}; +/** Staff will have to combine this with the stem_request, since the + number of flags that a stem wants to carry will determine the + number of beams. if #nplet# is set, the staff will try to put an + appropriate number over the beam + + [what to do if the nplet fields of start and stop conflict?] + */ + +///Start / stop a slur or a bracket. +struct Bracket_req : Span_req { + int nplet; +}; +/** +Start/stop a bracket at this note. if #nplet# is set, the staff will +try to put an appropriate number over the bracket +*/ + +/// a slur +struct Slur_req : Span_req { + +}; + +/// helper in the hierarchy +struct Dynamic { + Mtime subtime; +}; +/** Each dynamic is bound to one note ( a crescendo spanning multiple + notes is thought to be made of two "dynamics": a start and a stop). + Dynamic changes can occur in a smaller time than the length of its + note, therefore fore each Dynamic request carries a time, measured + from the start of its note. + + This subfield would come in handy, if mpp96 was adapted for midi + support. + + Dynamic should have been derived from request, but I don't want to + fuss with virtual baseclasses. */ + +/// do a crescendo +struct Cresc_req : Span_req, Dynamic { + +}; + +/// do a decrescendo +struct Decresc_req : Span_req, Dynamic { + +}; + +/// do a dynamic like "fff" or "mp" +struct Absdynamic_req : Request, Dynamic { + Loudness loudness; +}; + +#endif diff --git a/rhythmstaf.cc b/rhythmstaf.cc new file mode 100644 index 0000000000..61eca843f8 --- /dev/null +++ b/rhythmstaf.cc @@ -0,0 +1,150 @@ +#include "request.hh" +#include "debug.hh" +#include "linestaff.hh" +#include "staff.hh" +#include "pstaff.hh" +#include "pscore.hh" +#include "command.hh" +#include "molecule.hh" +#include "rhythmstaf.hh" + + + +Rhythmic_column::Rhythmic_column(Score_column*s, Rhythmic_staff *rs) + : Staff_column(s) +{ + the_note = 0; + staff_ = rs; +} + + +void +Rhythmic_staff::set_output(PScore* ps ) +{ + theline = new Linestaff(1); + pscore_ = ps; + pscore_->add(theline); +} + +// should integrate handling of BREAK commands into Staff_column +void +Rhythmic_column::process_commands( ) +{ + int breakstat = BREAK_END; + for (int i = 0 ; i < s_commands.sz(); i++) { + Command *com = s_commands[i]; + switch (com->code){ + case INTERPRET: + break; + case BREAK_PRE: + case BREAK_MIDDLE: + case BREAK_POST: + case BREAK_END: + score_column->set_breakable(); + breakstat = com->code; + break; + + case TYPESET: + typeset_command ( com , breakstat); + break; + default : + break; + } + + } +} +/** + accept: + + BREAK: all + TYPESET: bar, meter + + */ + + + +void +Rhythmic_column::process_requests() +{ + for (int i = 0 ; i < v_elts.sz(); i ++) + for (PCursor rqc(v_elts[i]->reqs); rqc.ok(); rqc++) { + Request *rq= rqc; + switch(rq->tag){ + case Request::NOTE: + case Request::REST: + if (the_note){ + WARN << "too many notes.\n"; + return; + } + the_note = rq; + break; + + default: + return; + } + } + +} + + +void +Rhythmic_column::typeset_command(Command *com, int breakst) +{ + Item *i = new Item; + const Symbol*s=0; + + if (com -> args[0] == "|" ) { + s = Symbol::find_bar("|"); + } else + assert(false); + + i->output=new Molecule(Atom(s)); + staff_->pscore_->typeset_item(i, score_column->pcol, + staff_->theline,breakst); +} + +void +Rhythmic_column::typeset_req(Request *rq) +{ + Item *i = new Item; + const Symbol*s=0; + + switch(rq->tag){ + case Request::NOTE: + s = Symbol::find_ball(rq->note()->balltype); + break; + case Request::REST: + s = Symbol::find_rest(rq->rest()->balltype); + break; + default: + assert(false); + break; + } + i->output = new Molecule(Atom(s)); + + staff_->pscore_->typeset_item(i, score_column->pcol, staff_->theline,0 ); +} + +void +Rhythmic_staff::grant_requests() +{ + for (PCursor cc(cols); cc.ok(); cc++) { + Rhythmic_column *rp = (Rhythmic_column*)*cc; + if (rp->the_note) + rp->typeset_req( rp->the_note); + } +} + +Staff_column* +Rhythmic_staff::create_col(Score_column*s) +{ + Rhythmic_column *rc = new Rhythmic_column(s,this); + + return rc; +} + +Staff * +get_new_rhythmstaff() +{ + return new Rhythmic_staff; +} diff --git a/rhythmstaf.hh b/rhythmstaf.hh new file mode 100644 index 0000000000..1e56355cd7 --- /dev/null +++ b/rhythmstaf.hh @@ -0,0 +1,31 @@ +struct Rhythmic_staff; + +/// column of Rhythmic_staff +struct Rhythmic_column : Staff_column { + // mega-stupido. only do notes, one at a time + Request *the_note; + Rhythmic_staff* staff_; + + /****************/ + + void typeset_req(Request *rq); + void take_request(Request *rq); + void typeset_command(Command *, int brs); + void process_commands( ); + void process_requests(); + + Rhythmic_column(Score_column*s,Rhythmic_staff*rs); +}; + + +/// simple percussion staff +struct Rhythmic_staff : Staff { + /// indirection to the PStaff. + PStaff *theline; + void set_output(PScore *); + void process_commands( PCursor &where); + + void grant_requests(); + Staff_column * create_col(Score_column*); +}; + diff --git a/score.cc b/score.cc new file mode 100644 index 0000000000..543aca0d5a --- /dev/null +++ b/score.cc @@ -0,0 +1,258 @@ +#include +#include "tstream.hh" +#include "score.hh" +#include "pscore.hh" +#include "staff.hh" +#include "misc.hh" +#include "debug.hh" + +void +Score::add(Command *c) +{ + commands_.bottom().add(new Command(*c)); +} + +void +Score::add(Staff*s) +{ + s->score_ = this; + staffs_.bottom().add(s); +} + + +void +Score::do_pcols() +{ + PCursor sc(cols_); + for (;sc.ok(); sc++) { + pscore_->add(sc->pcol); + } +} +/* + this sux. Really makeshift. + */ +void +Score::do_miscs() +{ + Command c; + + c.when = 0.0; + + c.code = BREAK_END; + commands_.top().insert(new Command(c)); + c.code = BREAK_POST; + commands_.top().insert(new Command(c)); + c.code = BREAK_MIDDLE; + commands_.top().insert(new Command(c)); + c.code = BREAK_PRE; + commands_.top().insert(new Command(c)); + + PCursor bot(commands_.bottom()); + c.when = last(); + while (bot.ok() && bot->when > c.when) { +// mtor <<"removing "<< bot->code <<" at " << bot->when<<'\n'; + bot.remove(); + bot = commands_.bottom(); + } + + c.code = BREAK_PRE; + bot.add(new Command(c)); + bot++; + c.code = BREAK_MIDDLE; + bot.add(new Command(c)); + bot++; + c.code = BREAK_POST; + bot.add(new Command(c)); + bot++; + c.code = BREAK_END; + bot.add(new Command(c)); + + commands_.OK(); +} + +Mtime +Score::last() const +{ + Mtime l = 0; + for (PCursor stc(staffs_); stc.ok(); stc++) { + l = MAX(l, stc->last()); + } + return l; +} +void +Score::clean_commands() +{ + Mtime l= last(); + for (PCursor cc(commands_); cc.ok(); cc++) { + if (cc->when > l){ + mtor << "remming \n"; + cc.remove(); + } + } +} +void +Score::process() +{ + do_miscs(); + + /// distribute commands to disciples + distribute_commands(); + + pscore_ = new PScore; + for (PCursor sc(staffs_); sc.ok(); sc++) { + sc->set_output(pscore_); + sc->process(); + } + do_pcols(); + calc_idealspacing(); + clean_cols(); + OK(); + // print(); + pscore_->calc_breaking(); +} + +// remove empty cols with no spacing attached. +/* should rethink ownership of cols + */ +void +Score::clean_cols() +{ + for (PCursor sc(staffs_); sc.ok(); sc++) + sc->clean_cols(); + for (PCursor c(cols_); c.ok(); c++) { + if (!c->pcol->used) { +// mtor << "removing : "; c->print(); + c.remove(); + } + } + + pscore_->clean_cols(); +} +/* this sux. We should have Score_column create the appropriate PCol. + Unfortunately, PCols don't know about their position. + */ +// todo +PCursor +Score::create_cols(Mtime w) +{ + Score_column* c1 = new Score_column(w); + Score_column* c2 = new Score_column(w); + + c1->musical = false; + c2->musical = true; + + PCursor scc(cols_); + + for (; scc.ok(); scc++) { + assert(scc->when != w); + if (scc->when > w) + break; + } + + if (!scc.ok()) { + cols_.bottom().add(c1); + cols_.bottom().add(c2); + scc = cols_.bottom(); + scc --; + } else { + scc.insert(c1); + scc.insert(c2); + scc -= 2; + } + return scc; +} + +Score_column* +Score::find_col(Mtime w,bool mus) +{ + PCursor scc(cols_); + for (; scc.ok(); scc++) { + if (scc->when == w && scc->musical == mus) + return scc; + if (scc->when > w) + break; + } + scc = create_cols(w); + if (mus) + scc++; + return scc; +} + +void +Score::distribute_commands(void) +{ + for (PCursor sc(staffs_); sc.ok(); sc++) { + sc->add_commands(commands_); + } +} + + +void +Score::output(String s) +{ + OK(); + mtor << "output to " << s << "...\n"; + + Tex_stream the_output(s); + the_output << "% Automatically generated by LilyPond 0.0 at"; + time_t t(time(0)); + the_output << ctime(&t)<<"\n"; + the_output << "% from input file ..\n"; + pscore_->output(the_output); +} + +void +Score::OK() const +{ + for (PCursor sc(staffs_); sc.ok(); sc++) { + sc->OK(); + assert(sc->score_ == this); + } + staffs_.OK(); + cols_.OK(); + for (PCursor cc(cols_); cc.ok() && (cc+1).ok(); cc++) { + assert(cc->when <= (cc+1)->when); + } + for (PCursor cc(commands_); cc.ok() && (cc+1).ok(); cc++) { + assert(cc->when <= (cc+1)->when); + } + +} + +void +Score::print() const +{ + mtor << "score {\n"; + for (PCursor sc(staffs_); sc.ok(); sc++) { + sc->print(); + } + for (PCursor sc(cols_); sc.ok(); sc++) { + sc->print(); + } + mtor << "}\n"; +} + +/****************************************************************/ + +Score_column::Score_column(Mtime w) +{ + when = w; + pcol = new PCol(0); + musical = false; +} + +bool +Score_column::used() { + return pcol->used; +} + +void +Score_column::print() const +{ + mtor << "Score_column { mus "<< musical <<" at " << when<<'\n'; + mtor << " # symbols: " << pcol->its.size() << "\n"; + mtor << "durations: [" ; + for (int i=0; i < durations.sz(); i++) + mtor << durations[i] << " "; + mtor << "]\n}\n"; +} diff --git a/score.hh b/score.hh new file mode 100644 index 0000000000..3e65d50d99 --- /dev/null +++ b/score.hh @@ -0,0 +1,83 @@ +#ifndef SCORE_HH +#define SCORE_HH + +#include "vray.hh" +#include "cols.hh" +#include "mtime.hh" +#include "command.hh" + +struct Score_column { + PCol * pcol; + svec durations; + Mtime when; + + /// + bool musical; + + + Score_column(Mtime when); + + static int compare(Score_column & c1, Score_column &c2) { + return sgn(c1.when - c2.when); + } + void set_breakable() { + pcol->set_breakable(); + } + bool used(); + void print() const; +}; +/** + + When typesetting hasn't started on PScore yet, the columns which + contain data have a rhythmical position. Score_column is the type + with a rhythmical time attached to it. The calculation of + idealspacing is done with data in these columns. (notably: the + #durations# field) + + */ + +instantiate_compare(Score_column&, Score_column::compare); + + +/// the total music def of one movement +struct Score { + /// staffs_ and commands_ form the problem definition. + PointerList staffs_; + PointerList commands_; + + /// "runtime" fields for setting up spacing + PointerList cols_; + PScore *pscore_; + + /****************************************************************/ + + /// add #Idealspacings# to #pscore_# + void calc_idealspacing(); + void process(); + + /// construction + void add_staff(Staff *st); + + void distribute_commands(); + /** add the score wide commands (bars, breaks) to each staff so + they can process (typeset) them if needed */ + void OK() const; + Score_column *find_col(Mtime,bool); + void do_pcols(); + void add(Command *); + void add(Staff*); + void output(String fn); + PCursor create_cols(Mtime); + void print() const; + void do_miscs() ; + Mtime last() const; + void clean_cols(); + void clean_commands(); + + void do_connect(PCol *c1, PCol *c2, Real d); + void connect_nonmus(PCol* c1, PCol *c2, Real d); +}; +/** + + */ +#endif diff --git a/smat.cc b/smat.cc new file mode 100644 index 0000000000..3830fd6825 --- /dev/null +++ b/smat.cc @@ -0,0 +1,187 @@ +#include "smat.hh" + +void +Full_storage::operator=(Full_storage const &fs) +{ + resize(fs.h, fs.w); + OK(); + fs.OK(); + for (int i=0; i= h && maxw >= w); + assert(h >= 0 && w >= 0); + assert(els||!maxh); + if (maxh>0) { // access outer elts. + Real *r = els[maxh -1]; + if (maxw>0) { + assert(r); + Real s = r[maxw -1]; + s = sin(s); + } + } +} +void +Full_storage::resize_cols(int newh) +{ + if (newh <= maxh) { + h=newh; + return; + } + + Real ** newa=new Real*[newh]; + int j=0; + for (; j < h; j++) + newa[j] = els[j]; + for (; j < newh; j++) + newa[j] = new Real[maxw]; + delete[] els; + els=newa; + + h = maxh = newh; +} + +void +Full_storage::resize_rows(int neww) +{ + if (neww <= maxw) { + w=neww; + return; + } + for (int i=0; i < maxh ; i++) { + Real* newa=new Real[neww]; + for (int k=0; k < w; k++) + newa[k] = els[i][k]; + + delete[] els[i]; + els[i] = newa; + } + w = maxw = neww; +} + +Full_storage::~Full_storage() { + for (int i=0; i < maxh; i++) + delete [] els[i]; + delete[] els; +} + +void +Full_storage::resize(int rows, int cols) +{ + OK(); + resize_cols(rows); + resize_rows(cols); + +} + + +bool +Full_storage::mult_ok(int i, int j) const +{ + return valid(i,j); +} + +bool +Full_storage::trans_ok(int i, int j) const +{ + return valid(i,j); +} + + +void +Full_storage::trans_next(int &i, int &j) const +{ + assert(trans_ok(i,j)); + i++; + if (i >= h) { + i=0; + j ++; + } +} + +void +Full_storage::mult_next(int &i, int &j) const +{ + assert(mult_ok(i,j)); + j++; + if (j >= w) { + j=0; + i++; + } +} + +void +Full_storage::delete_column(int k) +{ + assert(0 <= k &&k k ; i--) + for (int j=0; j +Full_storage::row(int n) const +{ + svec r; + for (int j = 0; j < w; j++) + r.add(els[n][j]); + return r; +} + +svec +Full_storage::column(int n) const +{ + + svec r; + for (int i = 0; i=0 && i < h) + && (j < w && j >=0); + } + + + void resize_rows(int); + void resize_cols(int); + +public: + virtual int rows() const { + return h; + } + virtual int cols() const { + return w; + } + + + virtual void set_size(int i, int j) + { + resize(i,j); //this could be more efficient. + } + + virtual void set_size(int i) { + set_size(i,i); + } + virtual void resize(int i, int j); + virtual void resize(int i) { + resize(i,i); + } + + virtual Real& elem(int i,int j) { + assert(valid(i,j)); + return els[i][j]; + } + virtual const Real& elem(int i, int j) const { + assert(valid(i,j)); + return els[i][j]; + } + virtual svec row(int i) const; + virtual svec column(int j) const; + + Full_storage() { + init(); + } + Full_storage(int i, int j) { + init(); + set_size(i,j); + } + Full_storage(Full_storage&); + Full_storage(int i) { + init(); + set_size(i); + } + void OK() const; + void operator=(Full_storage const &); + + virtual void insert_row(int k); + virtual void delete_row(int k); + virtual void delete_column(int k); + + + ~Full_storage(); + virtual bool mult_ok(int i, int j)const; + virtual void mult_next(int &i, int &j) const ; + virtual bool trans_ok(int i, int j) const; + virtual void trans_next(int &i, int &j) const; + virtual virtual_smat * clone(); +}; + +#endif diff --git a/staff.cc b/staff.cc new file mode 100644 index 0000000000..61699dccb2 --- /dev/null +++ b/staff.cc @@ -0,0 +1,179 @@ +#include "staff.hh" +#include "debug.hh" +#include "pscore.hh" + +void +Staff::clean_cols() +{ + PCursor stc(cols); + for(; stc.ok(); stc++){ + if (!stc->score_column->used()) + stc.remove(); + } +} + +/* + maak een staff column, met specs in args. + + (sorry wat is het vroeg vandaag..) + */ +Staff_column * +Staff::get_col(Mtime w, bool mus) +{ + Score_column* sc = score_->find_col(w,mus); + assert(sc->when == w); + PCursor stc(cols); + for (; stc.ok(); stc++) { + if (*sc < *stc->score_column) + break; + if (sc == stc->score_column) + return stc; + } + Staff_column* newst = create_col(sc); + + if (!stc.ok()) { + cols.bottom().add(newst); + return cols.bottom(); + } + + if (mus) { + stc.insert(newst); + return newst; + } + + if ((stc-1)->when() == newst->when()) { + stc--; + } + + stc.insert(newst); + + return newst; +} + + +void +Staff::add_voice(Voice *v) +{ + voices.bottom().add(v); +} + +/* + put all stuff grouped vertically in the Staff_cols + */ +void +Staff::setup_staffcols() +{ + + for (PCursor vc(voices); vc.ok(); vc++) { + + Mtime now = vc->start; + for (PCursor ve(vc->elts); ve.ok(); ve++) { + + Staff_column *sc=get_col(now,true); + sc->add(ve); + now += ve->duration; + } + } + + for (PCursor cc(commands); cc.ok(); cc++) { + Staff_column *sc=get_col(cc->when,false); + sc->s_commands.add(cc); + } +} + +/// merge commands from score +void +Staff::add_commands(PointerList const &cl) +{ + PCursor score_c(cl); + PCursor cc(commands); + + while (score_c.ok()) { + while (cc.ok() && cc->when <= score_c->when) + cc++; + + Command*nc = new Command (*(* score_c)); + if (cc.ok()) { + // cc->when > score_c->when + cc.insert( nc ); + } else { + commands.bottom().add( nc); + cc = commands.bottom(); + } + score_c++; + } + + // now integrate break commands with other commands. + // may be do this in derived functions. +} + +void +Staff::process() +{ + setup_staffcols(); + OK(); + for (PCursor sc(cols); sc.ok(); sc++) { + sc->process_commands(); + sc->process_requests(); + } + grant_requests(); +} + +void +Staff::OK() const +{ + cols.OK(); + commands.OK(); + voices.OK(); + assert(score_); + +} + + +Mtime +Staff::last() const { + Mtime l = 0.0; + for (PCursor vc(voices); vc.ok(); vc++) { + l = MAX(l, vc->last()); + } + return l; +} + + +void +Staff::print() const +{ + mtor << "Staff {\n"; + for (PCursor vc(voices); vc.ok(); vc++) { + vc->print(); + + } + mtor <<"}\n"; +} + +/****************************************************************/ + +bool +Staff_column::mus() const +{ + return score_column->musical; +} + +Mtime +Staff_column::when() const +{ + return score_column->when; +} +void +Staff_column::add(Voice_element*ve) +{ + Mtime d= ve->duration; + if (d){ + score_column->durations.add(d); + } + + v_elts.add(ve); +} +Staff_column::Staff_column(Score_column*s) { + score_column = s; +} diff --git a/staff.hh b/staff.hh new file mode 100644 index 0000000000..49439f373b --- /dev/null +++ b/staff.hh @@ -0,0 +1,78 @@ +#ifndef STAFF_HH +#define STAFF_HH + +#include "score.hh" +#include "voice.hh" +#include "command.hh" + +struct Staff_column { + Score_column *score_column; + + /// fields to collect data vertically. + svec v_elts; + svec s_commands; + + Staff_column(Score_column*s); + bool mus() const ; + Mtime when() const; + void add(Voice_element*ve); + /**************************************************************** + VIRTUAL + ****************************************************************/ + virtual void process_requests()=0; + virtual void process_commands()=0; + virtual ~Staff_column() { } +}; + + +/// base class for a collection of voices. +struct Staff { + /// synchronous horizontal stuff + PointerList voices; + + /// commands in chronological order + PointerList commands; + PointerList cols; + + /// indirections to the Score and PScore + Score *score_; + PScore *pscore_; + + void add_voice(Voice *v); + void add_staff_column(Staff_column *sp); + + + /// interpret all requests and add items to #destination#. + void process(); + /** + This routines calls virtual functions from Staff, to delegate the + interpretation of requests to a derived class of Staff */ + + + /**************************************************************** + VIRTUALS + ****************************************************************/ + + void setup_staffcols(); + + virtual void set_output(PScore * destination)=0; + virtual void grant_requests()=0; + + Staff_column * get_col(Mtime,bool); + + void add_commands(PointerListconst & sv); + /** + add all commands from sv. + + PRE + sv is time-ordered. + */ + virtual Staff_column * create_col(Score_column * )=0; + + void OK() const; + void print() const; + Mtime last() const; + void clean_cols() ; + virtual ~Staff() { } +}; +#endif diff --git a/suzan.ly b/suzan.ly new file mode 100644 index 0000000000..5dc5ca3354 --- /dev/null +++ b/suzan.ly @@ -0,0 +1,14 @@ +% testje + +score{ + rhythmstaff { + voice { $ c4 c2 c4 $ } + } + rhythmstaff { + voice { $ c2 c2 $ } + } + rhythmstaff { + voice { $ c4 c4 c4 c4 $ } + } + bar 2 +} diff --git a/symbol.cc b/symbol.cc new file mode 100644 index 0000000000..9a3e8cdf62 --- /dev/null +++ b/symbol.cc @@ -0,0 +1,7 @@ +#include "tex.hh" + +Symbol::Symbol(String s, Box b) + : tex(s), dim(b) +{ +} + diff --git a/symbol.ini b/symbol.ini new file mode 100644 index 0000000000..b66dbe1b5c --- /dev/null +++ b/symbol.ini @@ -0,0 +1,20 @@ +# TeXstring, xmin xmax ymin ymax + +table balls + 1 \wholeball -1 1 -1 1 + 2 \halfball -1 1 -1 1 + 4 \quartball -1 1 -1 1 +end + +table bars + | \maatstreep 0 1 0 10 +end + +table rests + 1 \wholerest -1 1 -1 1 + 2 \halfrest -1 1 -1 1 + 4 \quartrest -1 1 -1 1 + 8 \eighthrest -1 1 -1 1 + 16 \sixteenthrest -1 1 -1 1 + 32 \thirtysecondrest -1 1 -1 1 +end \ No newline at end of file diff --git a/symtable.cc b/symtable.cc new file mode 100644 index 0000000000..a63bc195c8 --- /dev/null +++ b/symtable.cc @@ -0,0 +1,109 @@ +#include "misc.hh" +#include "debug.hh" +#include "real.hh" +#include "tex.hh" +#include "assoc.hh" +#include "symtable.hh" +#include "const.hh" + +Symtable* +Symtables::operator()(String s) +{ + if (!done_reading){ // read on demand + *mlog << '(' << fname ; + read(); + done_reading = true; + *mlog << ")\n"; + } + return Assoc::operator[](s); +} + +void +Symtables::read() +{ + Text_db symini(fname); + while (1) { + if (symini.eof()) + break; + Text_record r( symini++); + if (!r.sz()) + continue; + + assert (r[0] == "table"); + + String tabnam = r[1]; + Symtable * sp = new Symtable; + while (1) { + r = symini++; + if (!r.sz()) + continue; + if (r[0] == "end") + break; + + assert(r.sz() == 6); + int i=0; + String id=r[i++]; + String tex=r[i++]; + svec dims; + for (int j=0; j < 4; j++) + dims.add( r[i++].fvalue() *1.0/CM_TO_PT); + + Symbol s(tex, Box(dims)); + (*sp)[id] = s; + } + (*this)[tabnam] = sp; + } +} + +Symtables the_sym_tables("symbol.ini"); + + +const Symbol* +Symbol::find_ball(int i) +{ + int j = intlog2(i); + if (j > 4) j = 4; + Symtable * st = the_sym_tables("balls"); + return &(*st)[String(j)]; + +} + +const Symbol* +Symbol::find_rest(int i) +{ + int j = intlog2(i); + return &(*the_sym_tables("rests"))[String(j)]; +} +const Symbol* +Symbol::find_bar(String s) +{ + return &(*the_sym_tables("bars"))[s]; +} +/****************************************************************/ +// bare bones. + +struct Linestaf_symbol : Stretchable_symbol { + int lines; + String operator ()(Real w); + Linestaf_symbol(int n) { lines = n;} +}; +// should be done in TeX +String +Linestaf_symbol::operator()(Real w) +{ + String s; + s += "\\hbox to 0pt{"; + s+= "\\vbox to 0pt{"; + for (int i=0; i { + +}; + + +struct Symtables : private Assoc { + String fname; + bool done_reading; + Symtables(String s) : fname (s) { + done_reading = false; + } + void read() ; + Symtable* operator()(String s); + +}; + diff --git a/table.cc b/table.cc new file mode 100644 index 0000000000..45e6f4a0e4 --- /dev/null +++ b/table.cc @@ -0,0 +1,27 @@ +#include "glob.hh" +#include "string.hh" +#include "keyword.hh" +#include "parser.hh" + +static Keyword_ent the_key_tab[]={ + "voice", VOICE, + "rhythmstaff", RHYTHMSTAFF, + "score", SCORE, + "bar", BAR, + 0,0 +} ; + + +int +lookup_keyword(String s) +{ + static Keyword_table table(the_key_tab); + return table.lookup(s); +} + +Identifier* +lookup_identifier(String s) +{ + assert(false); + return 0; +} diff --git a/template.cc b/template.cc new file mode 100644 index 0000000000..6d45473c1f --- /dev/null +++ b/template.cc @@ -0,0 +1,26 @@ +#include "line.hh" + +#include "list.hh" +#include "cols.hh" +#include "item.hh" +#include "request.hh" +#include "score.hh" +#include "command.hh" +#include "staff.hh" + +#include "list.cc" +#include "cursor.cc" + + +PL_instantiate(Line_of_staff); +PL_instantiate(Item); +PL_instantiate(Line_of_score); +PL_instantiate(Request); +PL_instantiate(Spanner); +PL_instantiate(PStaff); +PL_instantiate(Idealspacing); +PL_instantiate(PCol); +PL_instantiate(Command); +PL_instantiate(Score_column); + + diff --git a/template2.cc b/template2.cc new file mode 100644 index 0000000000..21e24f06c7 --- /dev/null +++ b/template2.cc @@ -0,0 +1,16 @@ +#include "line.hh" +#include "voice.hh" +#include "molecule.hh" +#include "staff.hh" + + + + +#include "list.cc" +#include "cursor.cc" +PL_instantiate(Staff_column); +PL_instantiate(Staff); +PL_instantiate(Voice_element); +PL_instantiate(Voice); +L_instantiate(Atom); + diff --git a/test.tex b/test.tex new file mode 100644 index 0000000000..b6bcb4fe13 --- /dev/null +++ b/test.tex @@ -0,0 +1,3 @@ +\input lilyponddefs +\input lelie.uit +\vfil\bye \ No newline at end of file diff --git a/tex.cc b/tex.cc new file mode 100644 index 0000000000..db789e47b3 --- /dev/null +++ b/tex.cc @@ -0,0 +1,26 @@ +#include "tex.hh" +#include "const.hh" +/* + #TeXstring# should generate a TeX string to typeset the object in + a hbox or vbox of exactly the objects' dimension. +*/ + + +/// #h# is in points +String +vstrut(Real h) +{ + return String("\\vrule height ") + h + "pt depth 0pt width 0pt"; +} + + +/// the staff with five lines. + struct Fiveline_staff: Stretchable_symbol { + String operator()(Real width) { + String s("\\normalebalk{ "); + s+=width * HOR_TO_PT; + s+= "pt}"; + return s; + } +}; + diff --git a/tex.hh b/tex.hh new file mode 100644 index 0000000000..04090aa510 --- /dev/null +++ b/tex.hh @@ -0,0 +1,54 @@ +#ifndef TEX_HH +#define TEX_HH +#include "string.hh" +#include "boxes.hh" + +struct Symbol { + String tex; + Box dim; + + Symbol (String, Box ); + static const Symbol*find_ball(int); + static const Symbol*find_rest(int); + static const Symbol*find_bar(String); + Symbol() { } +}; + +/// a symbol with a variable width +struct Stretchable_symbol { +public: + + /// return a string for a symbol in this width. + virtual String operator ()(Real width)=0; + + static const Stretchable_symbol* get_linestaff(int n); +}; + +/// anything which can be output +struct Output { + virtual String TeXstring() const=0; + /** generate a TeX string, which typesets the symbol. Vertical + base position is the "origin" of the staff + */ + virtual Box extent() const=0; +}; +/** + any output should (at least) be outputtable for TeX, and have a + dimension +*/ + + +/// an idea +struct Text_gob : Output { + String text; + // fonts, sizes, etc? + virtual String TeXstring() const; + virtual Box extent() const; +}; + + +/// #h# is in points +String vstrut(Real h); + + +#endif diff --git a/tstream.cc b/tstream.cc new file mode 100644 index 0000000000..78c9a72852 --- /dev/null +++ b/tstream.cc @@ -0,0 +1,64 @@ +#include +#include "tex.hh" +#include "tstream.hh" +#include "debug.hh" + +Tex_stream::Tex_stream(String filename) +{ + + os = new ofstream(filename); + if (!*os) + error("can't open " + filename); + nest_level = 0; + outputting_comment=false; +} + +Tex_stream::~Tex_stream() +{ + assert(nest_level == 0); + delete os; +} + +// print string. don't forget indent. +Tex_stream & +Tex_stream::operator<<(String s) +{ + for (const char *cp = s; *cp; cp++) { + if (outputting_comment) { + *os << *cp; + if (*cp == '\n') { + outputting_comment=false; + + } + continue; + } + switch(*cp) + { + case '%': + outputting_comment = true; + *os << *cp; + break; + case '{': + nest_level++; + *os << *cp; + break; + case '}': + nest_level--; + *os << *cp; + assert (nest_level >= 0); + /* FALL THROUGH */ + + case '\n': + *os << "%\n"; + *os << String(' ', nest_level); + break; + default: + *os << *cp; + break; + } + } + return *this; +} + + +/****************************************************************/ diff --git a/tstream.hh b/tstream.hh new file mode 100644 index 0000000000..07be0c1706 --- /dev/null +++ b/tstream.hh @@ -0,0 +1,27 @@ +#ifndef TSTREAM__HH +#define TSTREAM__HH + +#include +#include "string.hh" + +/// TeX output +struct Tex_stream { + bool outputting_comment; + ostream *os; + int nest_level; + + /// open a file for writing + Tex_stream(String filename); + + /// delegate conversion to string class. + Tex_stream &operator<<(String); + + /// close the file + ~Tex_stream(); +}; +/** + Use this class for writing to a TeX file. + It counts braces to prevent nesting errors, and + it will add a comment sign before each newline. + */ +#endif diff --git a/vector.cc b/vector.cc new file mode 100644 index 0000000000..be2423250d --- /dev/null +++ b/vector.cc @@ -0,0 +1,41 @@ +#include "debug.hh" +#include "vector.hh" +#include "string.hh" + +Vector::Vector(const Vector&n) + :dat(n.dat) + // this makes GCC 272 barf +{ + //dat = n.dat; +} + +Vector::operator String() const +{ + int i=0; + String s("vector ["); + for (; i < dim(); i++) { + s += String(dat[i], "%6f") + ' '; + } + s+="]"; + return s; +} + + +void +Vector::print() const +{ + mtor << *this<<'\n'; +} + +Vector +Vector::operator-() const +{ + Vector v(*this); v*=-1; return v; +} + +void +Vector::set_unit(int j) +{ + fill(0.0); + dat[j] = 1.0; +} diff --git a/vector.hh b/vector.hh new file mode 100644 index 0000000000..5be792c54b --- /dev/null +++ b/vector.hh @@ -0,0 +1,106 @@ +#ifndef VECTOR_HH +#define VECTOR_HH + +#include "glob.hh" +#include "vray.hh" + +/// a row of numbers +class Vector { + svec dat; +public: + void OK() const { dat.OK();} + int dim() const { return dat.sz(); } + Vector() { } + Vector(const Vector&n); + Vector(int n) { + dat.set_size(n); + fill(0); + } + void insert(Real v, int i) { + dat.insert(v,i); + } + void del(int i) { dat.del(i); } + operator String() const; + void fill(Real r) { + for (int i=0; i < dim(); i++) + dat[i] =r; + } + + void operator +=(Vector v) { + assert(v.dim() == dim()); + for (int i=0; i < dim(); i++) + dat[i] += v.dat[i]; + } + + void operator /=(Real a) { + (*this) *= 1/a; + } + + void operator *=(Real a) { + for (int i=0; i < dim(); i++) + dat[i] *= a; + } + + void operator -=(Vector v) { + assert(v.dim() == dim()); + for (int i=0; i < dim(); i++) + dat[i] -= v(i); + } + + Real &operator()(int i) { return dat[i]; } + Real operator()(int i) const { return dat[i]; } + Real elem(int i) { return dat[i]; } + Real operator *(Vector v) const { + Real ip=0; + assert(v.dim() == dim()); + for (int i=0; i < dim(); i++) + ip += dat[i] *v(i); + return ip; + } + Vector operator-() const; + Real norm() { + return sqrt(norm_sq() ); + } + Real norm_sq() { + return ((*this) * (*this)); + } + operator svec () { return dat; } + void print() const; + /// set to j-th element of unit-base + void set_unit(int j) ; +}; +/** + a vector. Storage is handled in svec, Vector only does the mathematics. + */ + +inline Vector +operator+(Vector a, Vector const &b) { + a += b; + return a; +} + +inline Vector +operator-(Vector a, Vector const &b) { + a -= b; + return a; +} + +inline Vector +operator*(Vector v, Real a) { + v *= a; + return v; +} + +inline Vector +operator*( Real a,Vector v) { + v *= a; + return v; +} + +inline Vector +operator/(Vector v,Real a) { + v *= 1/a; + return v; +} + +#endif diff --git a/version.cc b/version.cc new file mode 100644 index 0000000000..5a14bb442e --- /dev/null +++ b/version.cc @@ -0,0 +1,9 @@ +#include "version.hh" + +static char *s = "LilyPond version " VERSIONSTR " compiled on " __DATE__ " at " __DATE__ "\n"; + +char * +get_version() +{ + return s; +} diff --git a/voice.cc b/voice.cc new file mode 100644 index 0000000000..ac6d33166d --- /dev/null +++ b/voice.cc @@ -0,0 +1,56 @@ +#include "debug.hh" +#include "voice.hh" + +void +Voice_element::add(Request*r) +{ + if (r->tag == Request::NOTE ||r->tag == Request::REST) { + assert (!duration); + duration = r->duration(); + } + reqs.bottom().add(r); +} + +Voice::Voice() +{ + start = 0.0; +} + +void +Voice::add(Voice_element*v) +{ + elts.bottom().add(v); +} + +Voice_element::Voice_element() +{ + voice = 0; + group = 0; + duration = 0.0; +} + +void +Voice::print() const +{ + mtor << "start: "<< start< vec(elts); vec.ok(); vec++) + vec->print(); +} +void +Voice_element::print() const +{ + mtor << "voice_element { dur :"<< duration <<"\n"; + for (PCursor rc(reqs); rc.ok(); rc++) { + mtor << "reqtag: " << rc->tag< vec(elts); vec.ok(); vec++) + l += vec->duration; + return l; +} diff --git a/voice.hh b/voice.hh new file mode 100644 index 0000000000..35537525ec --- /dev/null +++ b/voice.hh @@ -0,0 +1,49 @@ +#ifndef VOICE_HH +#define VOICE_HH + +#include "mtime.hh" +#include "list.hh" +#include "request.hh" + +/// class for horizontal stuff. +struct Voice { + PointerList elts; + Mtime start; + + /****************/ + Mtime when(const Voice_element*)const; + Mtime last() const; + Voice(); + void add(Voice_element*); + void print() const; +}; +/** + + Voice is a ordered row of Voice_elements. It is strictly horizontal: + you cannot have two rhythmic elements running parallel in a Voice + + */ + +struct Voicegroup { + /// don't know how to identify these. +}; + +/// +struct Voice_element { + Mtime duration; + const Voicegroup *group; + const Voice *voice; + PointerList reqs; + + List granted_items; + List granted_spanners; + void add(Request*); + Voice_element(); + + void print ()const; +}; +/** Apart from being a container for the requests, Voice_element is + glue between related items and spanners, between requests and + (voice)groups + */ +#endif diff --git a/vsmat.hh b/vsmat.hh new file mode 100644 index 0000000000..4e1ea797b6 --- /dev/null +++ b/vsmat.hh @@ -0,0 +1,141 @@ +#ifndef VSMAT_HH +#define VSMAT_HH +#include "vray.hh" +#include "real.hh" +/// a matrix storage baseclass. +class virtual_smat { + + +public: + /// check invariants + virtual void OK() const=0; + + /// height of matrix + virtual int rows() const = 0; + + /// width of matrix + virtual int cols() const = 0; + + /// set the size. contents lost + virtual void set_size(int i, int j) = 0; + /** + PRE + i >=0, j>=0 + */ + + /// set the size to square dimen. contents lost + virtual void set_size(int i) = 0; + /** + PRE + i>=0 + */ + /// set the size to i + virtual void resize(int i, int j) = 0; + /** + + keep contents. If enlarged contents unspecified + + PRE + i>=0, j>=0 + + */ + + /// set the size to square dimen. contents kept + virtual void resize(int i) = 0; + /** + Keep contents. If enlarged contents are unspecified + + PRE + i>=0 + */ + + /// access an element + virtual Real& elem(int i,int j) = 0; + /** + access an element. + + Generate an errormessage, if this happens + in the 0-part of a sparse matrix. + */ + + /// access a element, no modify + virtual const Real& elem(int i, int j) const = 0; + +#if 1 + virtual svec row(int i) const = 0; + virtual svec column(int j) const = 0; +#endif + + /// add a row + virtual void insert_row(int k)=0; + /** + add a row to the matrix before row k. Contents + of added row are unspecified + + 0 <= k <= rows() + */ + + /// delete a row + virtual void delete_row(int k)=0; + /** + delete a row from this matrix. + + PRE + 0 <= k < rows(); + */ + virtual void delete_column(int k)=0; + virtual ~virtual_smat() { } + virtual virtual_smat *clone()=0; + + + /// is there a next? + virtual bool mult_ok(int i, int j) const=0; + /** + at end of matrix? when doing loop + + for(i=0; i