lotus / inc /measureLayout.ts
k-l-lambda's picture
commit lotus dist.
d605f27
raw
history blame
5.19 kB
import {SimpleClass} from "./jsonRecovery";
enum LayoutType {
Ordinary = "ordinary",
Full = "full",
Conservative = "conservative",
Once = "once",
};
interface MeasureLayout {
serialize (type: LayoutType): number[];
seq: MeasureSeq;
code: string;
};
export type MeasureSeq = MeasureLayout[];
const spreadMeasureSeq = (seq: MeasureSeq, type: LayoutType = LayoutType.Ordinary): number[] => [].concat(...seq.map(layout => layout.serialize(type)));
const seqToCode = (seq: MeasureSeq, {withBrackets = false}: {withBrackets?: boolean} = {}): string => {
//const code = seq.map(layout => layout.code).join(", ");
let code = "";
let inRange = false;
for (let i = 0; i < seq.length; ++i) {
const middle = seq[i - 1] instanceof SingleMLayout && seq[i] instanceof SingleMLayout && seq[i + 1] instanceof SingleMLayout;
if (middle) {
if (!inRange) {
code += "..";
inRange = true;
}
}
else {
if (i > 0 && !inRange)
code += ", ";
inRange = false;
code += seq[i].code;
}
}
return withBrackets ? `[${code}]` : code;
};
class SingleMLayout extends SimpleClass implements MeasureLayout {
static className = "SingleMLayout";
measure: number;
static from (measure: number) {
const layout = new SingleMLayout();
layout.measure = measure;
return layout;
}
serialize (): number[] {
return [this.measure];
}
get seq (): MeasureSeq {
return [this];
}
get code (): string {
return this.measure.toString();
}
};
class BlockMLayout extends SimpleClass implements MeasureLayout {
static className = "BlockMLayout";
seq: MeasureSeq;
static trimSeq (seq: MeasureSeq): MeasureSeq {
const seq2 = [];
for (const layout of seq) {
if (layout instanceof BlockMLayout) {
for (const sub of layout.seq)
seq2.push(sub);
}
else
seq2.push(layout);
}
// reduce duplicated or backwards single measures
const seq3 = [];
let measure = null;
for (const layout of seq2) {
if (layout instanceof SingleMLayout) {
if (layout.measure > measure) {
seq3.push(layout);
measure = layout.measure;
}
}
else
seq3.push(layout);
}
return seq3;
}
static fromSeq (seq: MeasureSeq): BlockMLayout {
const layout = new BlockMLayout();
layout.seq = BlockMLayout.trimSeq(seq);
return layout;
}
serialize (type: LayoutType): number[] {
return spreadMeasureSeq(this.seq, type);
}
get code (): string {
return seqToCode(this.seq, {withBrackets: true});
}
};
class VoltaMLayout extends SimpleClass implements MeasureLayout {
static className = "VoltaMLayout";
times: number;
body: MeasureSeq;
alternates: MeasureSeq[];
serialize (type: LayoutType): number[] {
const bodySeq = spreadMeasureSeq(this.body);
if (this.alternates) {
const alternateSeqs = this.alternates.map(seq => spreadMeasureSeq(seq));
const lastAlternateSeq = alternateSeqs[alternateSeqs.length - 1];
switch (type) {
case LayoutType.Ordinary:
return bodySeq.concat(...alternateSeqs);
case LayoutType.Conservative:
case LayoutType.Full: {
const priorSeq = [].concat(...Array(this.times - 1).fill(null).map((_, i) => [
...bodySeq,
...alternateSeqs[i % (this.times - 1)],
]));
return [
...priorSeq,
...bodySeq,
...lastAlternateSeq,
];
}
case LayoutType.Once:
return [
...bodySeq,
...lastAlternateSeq,
];
}
}
else {
switch (type) {
case LayoutType.Ordinary:
case LayoutType.Conservative:
case LayoutType.Once:
return bodySeq;
case LayoutType.Full:
return [].concat(...Array(this.times).fill(null).map(() => bodySeq));
}
}
console.warn("the current case not handled:", type, this);
}
get seq (): MeasureSeq {
const alternates = this.alternates ? this.alternates[this.alternates.length - 1] : [];
return [
...this.body,
...alternates,
];
}
get code (): string {
const body = seqToCode(this.body, {withBrackets: true});
let code = `${this.times}*${body}`;
if (this.alternates)
code += "{" + this.alternates.map(seq => seqToCode(seq, {withBrackets: seq.length > 1})).join(", ") +"}";
return code;
}
};
class ABAMLayout extends SimpleClass implements MeasureLayout {
static className = "ABAMLayout";
main: MeasureLayout;
rest: MeasureSeq;
serialize (type: LayoutType): number[] {
const seqA = this.main.serialize(type);
const seqA_ = spreadMeasureSeq(this.main.seq, LayoutType.Once);
const seqB = spreadMeasureSeq(this.rest, type);
switch (type) {
case LayoutType.Ordinary: // A B
return [
...seqA,
...seqB,
];
case LayoutType.Once: // B A'
return [
...seqB,
...seqA_,
];
case LayoutType.Conservative: // A B A'
case LayoutType.Full: // A B A'
return [
...seqA,
...seqB,
...seqA_,
];
default:
console.warn("the current case not handled:", type, this);
}
}
get seq (): MeasureSeq {
return [
this.main,
...this.rest,
];
}
get code (): string {
return "<" + this.main.code + ", " + seqToCode(this.rest) + ">";
}
};
export {
LayoutType,
MeasureLayout,
SingleMLayout,
BlockMLayout,
VoltaMLayout,
ABAMLayout,
};