From 675bd3e6ea001c3af033b51a6e2eeab6a5809e86 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 9 Oct 1996 14:04:47 +0200 Subject: [PATCH] release: 0.0.1 --- Makefile | 78 +++++++++ Sources.make | 20 +++ boxes.cc | 22 +++ boxes.hh | 82 +++++++++ break.cc | 106 ++++++++++++ calcideal.cc | 64 +++++++ choleski.cc | 85 ++++++++++ choleski.hh | 46 +++++ cols.cc | 77 +++++++++ cols.hh | 96 +++++++++++ command.cc | 13 ++ command.hh | 69 ++++++++ const.hh | 11 ++ debug.cc | 15 ++ debug.hh | 25 +++ depend | 179 ++++++++++++++++++++ dstream.cc | 46 +++++ dstream.hh | 29 ++++ flower/Makefile | 43 +++++ flower/assoc.hh | 73 ++++++++ flower/compare.hh | 24 +++ flower/cursor.cc | 75 ++++++++ flower/cursor.hh | 125 ++++++++++++++ flower/cursor.inl | 100 +++++++++++ flower/dataf.cc | 125 ++++++++++++++ flower/findcurs.hh | 12 ++ flower/findcurs.inl | 37 ++++ flower/handle.hh | 57 +++++++ flower/lgetopt.cc | 214 +++++++++++++++++++++++ flower/lgetopt.hh | 90 ++++++++++ flower/link.hh | 38 +++++ flower/link.inl | 100 +++++++++++ flower/list.cc | 88 ++++++++++ flower/list.hh | 96 +++++++++++ flower/list.inl | 144 ++++++++++++++++ flower/string.cc | 395 +++++++++++++++++++++++++++++++++++++++++++ flower/string.hh | 172 +++++++++++++++++++ flower/stringutil.hh | 245 +++++++++++++++++++++++++++ flower/textdb.cc | 48 ++++++ flower/textdb.hh | 57 +++++++ flower/textstr.hh | 114 +++++++++++++ flower/unionfind.cc | 35 ++++ flower/unionfind.hh | 25 +++ flower/vray.hh | 171 +++++++++++++++++++ glob.hh | 9 + globvars.hh | 1 + item.cc | 49 ++++++ item.hh | 47 +++++ keyword.cc | 79 +++++++++ keyword.hh | 17 ++ lexer.hh | 14 ++ lexer.l | 184 ++++++++++++++++++++ lilyponddefs.tex | 29 ++++ line.cc | 94 ++++++++++ line.hh | 52 ++++++ linespace.cc | 249 +++++++++++++++++++++++++++ linespace.hh | 98 +++++++++++ linestaff.cc | 9 + linestaff.hh | 8 + main.cc | 54 ++++++ make_version | 8 + matrix.cc | 281 ++++++++++++++++++++++++++++++ matrix.hh | 141 +++++++++++++++ misc.cc | 32 ++++ misc.hh | 8 + molecule.cc | 87 ++++++++++ molecule.hh | 50 ++++++ mtime.hh | 8 + note.cc | 130 ++++++++++++++ notename.cc | 30 ++++ notename.hh | 2 + parseconstruct.hh | 7 + parser.y | 121 +++++++++++++ proto.hh | 76 +++++++++ pscore.cc | 99 +++++++++++ pscore.hh | 80 +++++++++ pstaff.cc | 13 ++ pstaff.hh | 19 +++ qlp.cc | 137 +++++++++++++++ qlp.hh | 89 ++++++++++ qlpsolve.cc | 230 +++++++++++++++++++++++++ qlpsolve.hh | 78 +++++++++ real.hh | 25 +++ request.cc | 70 ++++++++ request.hh | 195 +++++++++++++++++++++ rhythmstaf.cc | 150 ++++++++++++++++ rhythmstaf.hh | 31 ++++ score.cc | 258 ++++++++++++++++++++++++++++ score.hh | 83 +++++++++ smat.cc | 187 ++++++++++++++++++++ smat.hh | 93 ++++++++++ staff.cc | 179 ++++++++++++++++++++ staff.hh | 78 +++++++++ suzan.ly | 14 ++ symbol.cc | 7 + symbol.ini | 20 +++ symtable.cc | 109 ++++++++++++ symtable.hh | 16 ++ table.cc | 27 +++ template.cc | 26 +++ template2.cc | 16 ++ test.tex | 3 + tex.cc | 26 +++ tex.hh | 54 ++++++ tstream.cc | 64 +++++++ tstream.hh | 27 +++ vector.cc | 41 +++++ vector.hh | 106 ++++++++++++ version.cc | 9 + voice.cc | 56 ++++++ voice.hh | 49 ++++++ vsmat.hh | 141 +++++++++++++++ warn.cc | 18 ++ 113 files changed, 8663 insertions(+) create mode 100644 Makefile create mode 100644 Sources.make create mode 100644 boxes.cc create mode 100644 boxes.hh create mode 100644 break.cc create mode 100644 calcideal.cc create mode 100644 choleski.cc create mode 100644 choleski.hh create mode 100644 cols.cc create mode 100644 cols.hh create mode 100644 command.cc create mode 100644 command.hh create mode 100644 const.hh create mode 100644 debug.cc create mode 100644 debug.hh create mode 100644 depend create mode 100644 dstream.cc create mode 100644 dstream.hh create mode 100644 flower/Makefile create mode 100644 flower/assoc.hh create mode 100644 flower/compare.hh create mode 100644 flower/cursor.cc create mode 100644 flower/cursor.hh create mode 100644 flower/cursor.inl create mode 100644 flower/dataf.cc create mode 100644 flower/findcurs.hh create mode 100644 flower/findcurs.inl create mode 100644 flower/handle.hh create mode 100644 flower/lgetopt.cc create mode 100644 flower/lgetopt.hh create mode 100644 flower/link.hh create mode 100644 flower/link.inl create mode 100644 flower/list.cc create mode 100644 flower/list.hh create mode 100644 flower/list.inl create mode 100644 flower/string.cc create mode 100644 flower/string.hh create mode 100644 flower/stringutil.hh create mode 100644 flower/textdb.cc create mode 100644 flower/textdb.hh create mode 100644 flower/textstr.hh create mode 100644 flower/unionfind.cc create mode 100644 flower/unionfind.hh create mode 100644 flower/vray.hh create mode 100644 glob.hh create mode 100644 globvars.hh create mode 100644 item.cc create mode 100644 item.hh create mode 100644 keyword.cc create mode 100644 keyword.hh create mode 100644 lexer.hh create mode 100644 lexer.l create mode 100644 lilyponddefs.tex create mode 100644 line.cc create mode 100644 line.hh create mode 100644 linespace.cc create mode 100644 linespace.hh create mode 100644 linestaff.cc create mode 100644 linestaff.hh create mode 100644 main.cc create mode 100755 make_version create mode 100644 matrix.cc create mode 100644 matrix.hh create mode 100644 misc.cc create mode 100644 misc.hh create mode 100644 molecule.cc create mode 100644 molecule.hh create mode 100644 mtime.hh create mode 100644 note.cc create mode 100644 notename.cc create mode 100644 notename.hh create mode 100644 parseconstruct.hh create mode 100644 parser.y create mode 100644 proto.hh create mode 100644 pscore.cc create mode 100644 pscore.hh create mode 100644 pstaff.cc create mode 100644 pstaff.hh create mode 100644 qlp.cc create mode 100644 qlp.hh create mode 100644 qlpsolve.cc create mode 100644 qlpsolve.hh create mode 100644 real.hh create mode 100644 request.cc create mode 100644 request.hh create mode 100644 rhythmstaf.cc create mode 100644 rhythmstaf.hh create mode 100644 score.cc create mode 100644 score.hh create mode 100644 smat.cc create mode 100644 smat.hh create mode 100644 staff.cc create mode 100644 staff.hh create mode 100644 suzan.ly create mode 100644 symbol.cc create mode 100644 symbol.ini create mode 100644 symtable.cc create mode 100644 symtable.hh create mode 100644 table.cc create mode 100644 template.cc create mode 100644 template2.cc create mode 100644 test.tex create mode 100644 tex.cc create mode 100644 tex.hh create mode 100644 tstream.cc create mode 100644 tstream.hh create mode 100644 vector.cc create mode 100644 vector.hh create mode 100644 version.cc create mode 100644 voice.cc create mode 100644 voice.hh create mode 100644 vsmat.hh create mode 100644 warn.cc 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