linked-rolls 0.19.1 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Collation.d.ts +1 -3
- package/lib/Collation.js +9 -11
- package/lib/Edition.d.ts +1 -9
- package/lib/EditionView.d.ts +54 -6
- package/lib/EditionView.js +93 -20
- package/lib/Emulation.js +1 -1
- package/lib/RollCopy.d.ts +17 -2
- package/lib/RollCopy.js +18 -2
- package/lib/TrackerBar.d.ts +17 -2
- package/lib/TrackerBar.js +22 -16
- package/lib/Version.d.ts +49 -1
- package/lib/asJsonLd.js +23 -8
- package/lib/constraints.d.ts +8 -19
- package/lib/constraints.js +71 -15
- package/lib/context.d.ts +8 -1
- package/lib/context.js +8 -1
- package/lib/editionOps.d.ts +3 -1
- package/lib/editionOps.js +77 -15
- package/lib/importJsonLd.js +4 -2
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/migrate.js +85 -11
- package/lib/readers/phillipsEroll.d.ts +3 -5
- package/lib/readers/phillipsEroll.js +6 -7
- package/lib/readers/spencerBar.d.ts +4 -5
- package/lib/readers/spencerBar.js +7 -8
- package/lib/readers/stanfordAton.d.ts +4 -7
- package/lib/readers/stanfordAton.js +4 -5
- package/lib/reservations.d.ts +1 -1
- package/lib/reservations.js +24 -2
- package/lib/schema.json +27 -5
- package/lib/spec/context.json +1 -0
- package/lib/spec/welte-licensee.context.json +10 -0
- package/lib/substitution.d.ts +73 -0
- package/lib/substitution.js +175 -0
- package/package.json +1 -1
package/lib/Collation.d.ts
CHANGED
|
@@ -13,9 +13,7 @@ export interface CollationTolerance {
|
|
|
13
13
|
}
|
|
14
14
|
export declare const defaultCollationTolerance: CollationTolerance;
|
|
15
15
|
/** Where a symbol lies along the roll, as its carriers put it, or nothing for a symbol without a place. */
|
|
16
|
-
export type Locate = (symbol: AnySymbol) => Readonly<
|
|
17
|
-
horizontal: HorizontalSpan;
|
|
18
|
-
}> | undefined;
|
|
16
|
+
export type Locate = (symbol: AnySymbol) => Readonly<HorizontalSpan> | undefined;
|
|
19
17
|
/**
|
|
20
18
|
* Two symbols collate when they are of one kind, say the same thing
|
|
21
19
|
* (pitch, or expression type and scope), and lie at about the same
|
package/lib/Collation.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
+
import { keyOf } from "./TrackerBar";
|
|
1
2
|
import { distance, mm } from "./Quantity";
|
|
2
3
|
import { partitionPoint } from "./sorted";
|
|
3
4
|
export const defaultCollationTolerance = { toleranceStart: mm(5), toleranceEnd: mm(5) };
|
|
4
5
|
/**
|
|
5
6
|
* What a symbol says, as a key: the pitch of a note, the type and scope
|
|
6
|
-
* of an expression. Symbols collate within one key only.
|
|
7
|
+
* of an expression. Symbols collate within one key only. This is the
|
|
8
|
+
* same key the tracker bars are indexed by, so two symbols collate
|
|
9
|
+
* exactly where two bars would read them as the same thing, which is
|
|
10
|
+
* what carries a note across a transfer between systems.
|
|
7
11
|
*/
|
|
8
|
-
const kindOf = (symbol) =>
|
|
9
|
-
switch (symbol.type) {
|
|
10
|
-
case 'note': return `note ${symbol.pitch}`;
|
|
11
|
-
case 'expression': return `expression ${symbol.scope} ${symbol.expressionType}`;
|
|
12
|
-
case 'text': return 'text';
|
|
13
|
-
}
|
|
14
|
-
};
|
|
12
|
+
const kindOf = (symbol) => symbol.type === 'text' ? 'text' : keyOf(symbol);
|
|
15
13
|
const nearby = (here, there, tolerance) => distance(here.from, there.from) <= tolerance.toleranceStart
|
|
16
14
|
&& distance(here.to, there.to) <= tolerance.toleranceEnd;
|
|
17
15
|
/**
|
|
@@ -22,12 +20,12 @@ const nearby = (here, there, tolerance) => distance(here.from, there.from) <= to
|
|
|
22
20
|
export const isCollatable = (a, b, locate, tolerance = defaultCollationTolerance) => {
|
|
23
21
|
if (kindOf(a) !== kindOf(b))
|
|
24
22
|
return false;
|
|
25
|
-
const here = locate(a)
|
|
26
|
-
const there = locate(b)
|
|
23
|
+
const here = locate(a);
|
|
24
|
+
const there = locate(b);
|
|
27
25
|
return here !== undefined && there !== undefined && nearby(here, there, tolerance);
|
|
28
26
|
};
|
|
29
27
|
const placed = (symbols, locate) => symbols.flatMap((symbol, index) => {
|
|
30
|
-
const horizontal = locate(symbol)
|
|
28
|
+
const horizontal = locate(symbol);
|
|
31
29
|
return horizontal ? [{ symbol, index, horizontal }] : [];
|
|
32
30
|
});
|
|
33
31
|
const groupBy = (items, keyOf) => items.reduce((groups, item) => {
|
package/lib/Edition.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { RollCopy } from "./RollCopy";
|
|
|
2
2
|
import { Version } from "./Version";
|
|
3
3
|
import { CollationTolerance } from "./Collation";
|
|
4
4
|
import { DateAssignment, ObjectAssumption } from "./Assumption";
|
|
5
|
-
import {
|
|
5
|
+
import { Editor, Person, Place } from "./Agent";
|
|
6
6
|
import { RollTempo } from "./ReproducingSystem";
|
|
7
7
|
/**
|
|
8
8
|
* This type describes the creation of an edition,
|
|
@@ -91,14 +91,6 @@ export interface Roll {
|
|
|
91
91
|
* @see dcterms:identifier
|
|
92
92
|
*/
|
|
93
93
|
catalogueNumber: string;
|
|
94
|
-
/**
|
|
95
|
-
* The reproducing system the roll was cut for. A system the
|
|
96
|
-
* type vocabulary knows carries the IRI of its concept as `id`,
|
|
97
|
-
* from which the export takes the system's own context, so that
|
|
98
|
-
* the expression types are read as that system's.
|
|
99
|
-
* @see crm:P2 has type
|
|
100
|
-
*/
|
|
101
|
-
system: Concept;
|
|
102
94
|
/**
|
|
103
95
|
* @see lrmoo:R19i was realised through
|
|
104
96
|
*/
|
package/lib/EditionView.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Edition } from "./Edition";
|
|
2
|
-
import { HorizontalSpan,
|
|
2
|
+
import { HorizontalSpan, AnyFeature } from "./Feature";
|
|
3
3
|
import { AnySymbol, Expression, Note } from "./Symbol";
|
|
4
4
|
import { Version } from "./Version";
|
|
5
5
|
import { NegotiatedEvent } from "./ReproducingSystem";
|
|
6
|
+
import { TrackerBar } from "./TrackerBar";
|
|
7
|
+
import { RollCopy } from "./RollCopy";
|
|
6
8
|
import { Millimeters } from "./Quantity";
|
|
7
9
|
export type Path = (string | number)[];
|
|
8
10
|
export declare const getAt: <T>(path: Path, obj: unknown) => T | undefined;
|
|
@@ -20,6 +22,8 @@ export declare class EditionView {
|
|
|
20
22
|
* Map from id to paths where it is referenced
|
|
21
23
|
*/
|
|
22
24
|
private readonly links;
|
|
25
|
+
/** Built when a copy is first asked for, the walk not recording it. */
|
|
26
|
+
private copiesByFeature?;
|
|
23
27
|
constructor(edition: Edition);
|
|
24
28
|
atPath<T>(path: Path): T | null;
|
|
25
29
|
indexObjects(): void;
|
|
@@ -31,11 +35,46 @@ export declare class EditionView {
|
|
|
31
35
|
/** The version and its ancestors, from the version up to the root. */
|
|
32
36
|
private lineageOf;
|
|
33
37
|
carriersOf(symbol: AnySymbol): Readonly<AnyFeature>[];
|
|
38
|
+
/** The copy a feature sits on, a patch and everything it bears included. */
|
|
39
|
+
copyOf(featureId: string): Readonly<RollCopy> | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* How a place on the edition's shared axis relates to the paper of
|
|
42
|
+
* this version: place × factor = millimetres of its own paper.
|
|
43
|
+
*
|
|
44
|
+
* Copies cut for different systems are scaled onto one axis so that
|
|
45
|
+
* they can be collated at all, which leaves a version of another
|
|
46
|
+
* system carrying places in the axis copy's millimetres. A
|
|
47
|
+
* performance needs the paper the roll actually ran on, and the
|
|
48
|
+
* factor is the inverse of the scale `alignCopy` recorded.
|
|
49
|
+
*
|
|
50
|
+
* It is read only from the copies of the version's own system, since
|
|
51
|
+
* under the shared axis a green version's notes are carried by red
|
|
52
|
+
* copies too and those say nothing about green paper. A copy whose
|
|
53
|
+
* scale is put down to its own paper having stretched is left out as
|
|
54
|
+
* well: that is a fact about the one exemplar, not about the speed
|
|
55
|
+
* the system's rolls were cut at. Where what remains disagrees,
|
|
56
|
+
* `constraintProblems` reports it rather than averaging it away.
|
|
57
|
+
*/
|
|
58
|
+
toOwnPaperOf(version: Readonly<Version>): number | undefined;
|
|
59
|
+
/** The scales of the version's own copies that are not their own paper stretch. */
|
|
60
|
+
speedScalesIn(version: Readonly<Version>): number[];
|
|
61
|
+
/** The copies of the version's own system that carry any of its symbols. */
|
|
62
|
+
copiesOwning(version: Readonly<Version>): Readonly<RollCopy>[];
|
|
34
63
|
predecessorOf(versionId: string): Readonly<Version> | undefined;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Where along the roll the symbol lies, as its carriers put it, or
|
|
66
|
+
* nothing for a symbol no copy carries.
|
|
67
|
+
*
|
|
68
|
+
* Only the place is measured. Which track the symbol sits on is not
|
|
69
|
+
* a measurement but a question for a tracker bar, since a note of
|
|
70
|
+
* one pitch sits on exactly one position of a given bar: ask
|
|
71
|
+
* `bar.positionOf`. The carriers cannot answer it, because a copy
|
|
72
|
+
* cut for another system numbers its tracks differently, and one
|
|
73
|
+
* symbol may be carried by copies of both — averaging a red carrier
|
|
74
|
+
* on track 47 with a green one on 45 would give 46, a legal
|
|
75
|
+
* position a semitone away on either bar.
|
|
76
|
+
*/
|
|
77
|
+
placeOf(symbol: AnySymbol): Readonly<HorizontalSpan> | undefined;
|
|
39
78
|
/** Where the symbol begins, as the mean onset of its carriers, or nothing for a symbol without a place. */
|
|
40
79
|
onsetOf(symbol: AnySymbol): Millimeters | undefined;
|
|
41
80
|
/** The symbols by onset, those without a place first; symbols at one place keep their order. */
|
|
@@ -51,5 +90,14 @@ export declare class EditionView {
|
|
|
51
90
|
withGenerations(): Array<Version & {
|
|
52
91
|
generation: number;
|
|
53
92
|
}>;
|
|
54
|
-
|
|
93
|
+
/**
|
|
94
|
+
* The symbol as a performance needs it: where it lies, and the
|
|
95
|
+
* position the performing bar reads it on.
|
|
96
|
+
*
|
|
97
|
+
* Nothing where that bar reads nothing of it, which is the case a
|
|
98
|
+
* transfer between systems leaves behind: a red `ForzandoOn` a
|
|
99
|
+
* green version still inherits cannot be performed on a green
|
|
100
|
+
* machine, and an edit has yet to say what took its place.
|
|
101
|
+
*/
|
|
102
|
+
simplifySymbol(symbol: Note | Expression, bar: TrackerBar): NegotiatedEvent | null;
|
|
55
103
|
}
|
package/lib/EditionView.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { withBorneFeatures } from "./Feature";
|
|
1
2
|
import { deletedBy, insertedBy } from "./Version";
|
|
3
|
+
import { systemIdOf } from "./TrackerBar";
|
|
4
|
+
import { isPaperStretch } from "./RollCopy";
|
|
2
5
|
import { idOf, idsOf } from "./Assumption";
|
|
3
6
|
import { mean } from "./Quantity";
|
|
4
7
|
export const getAt = (path, obj) => {
|
|
@@ -58,6 +61,13 @@ export class EditionView {
|
|
|
58
61
|
writable: true,
|
|
59
62
|
value: new Map()
|
|
60
63
|
});
|
|
64
|
+
/** Built when a copy is first asked for, the walk not recording it. */
|
|
65
|
+
Object.defineProperty(this, "copiesByFeature", {
|
|
66
|
+
enumerable: true,
|
|
67
|
+
configurable: true,
|
|
68
|
+
writable: true,
|
|
69
|
+
value: void 0
|
|
70
|
+
});
|
|
61
71
|
this.edition = edition;
|
|
62
72
|
this.indexObjects();
|
|
63
73
|
}
|
|
@@ -141,35 +151,87 @@ export class EditionView {
|
|
|
141
151
|
carriersOf(symbol) {
|
|
142
152
|
return this.getAll(idsOf(symbol.carriers));
|
|
143
153
|
}
|
|
154
|
+
/** The copy a feature sits on, a patch and everything it bears included. */
|
|
155
|
+
copyOf(featureId) {
|
|
156
|
+
if (!this.copiesByFeature) {
|
|
157
|
+
this.copiesByFeature = new Map(this.edition.copies.flatMap(copy => copy.features
|
|
158
|
+
.flatMap(withBorneFeatures)
|
|
159
|
+
.map(feature => [feature.id, copy])));
|
|
160
|
+
}
|
|
161
|
+
return this.copiesByFeature.get(featureId);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* How a place on the edition's shared axis relates to the paper of
|
|
165
|
+
* this version: place × factor = millimetres of its own paper.
|
|
166
|
+
*
|
|
167
|
+
* Copies cut for different systems are scaled onto one axis so that
|
|
168
|
+
* they can be collated at all, which leaves a version of another
|
|
169
|
+
* system carrying places in the axis copy's millimetres. A
|
|
170
|
+
* performance needs the paper the roll actually ran on, and the
|
|
171
|
+
* factor is the inverse of the scale `alignCopy` recorded.
|
|
172
|
+
*
|
|
173
|
+
* It is read only from the copies of the version's own system, since
|
|
174
|
+
* under the shared axis a green version's notes are carried by red
|
|
175
|
+
* copies too and those say nothing about green paper. A copy whose
|
|
176
|
+
* scale is put down to its own paper having stretched is left out as
|
|
177
|
+
* well: that is a fact about the one exemplar, not about the speed
|
|
178
|
+
* the system's rolls were cut at. Where what remains disagrees,
|
|
179
|
+
* `constraintProblems` reports it rather than averaging it away.
|
|
180
|
+
*/
|
|
181
|
+
toOwnPaperOf(version) {
|
|
182
|
+
const scales = this.speedScalesIn(version);
|
|
183
|
+
return scales.length === 1 ? 1 / scales[0] : undefined;
|
|
184
|
+
}
|
|
185
|
+
/** The scales of the version's own copies that are not their own paper stretch. */
|
|
186
|
+
speedScalesIn(version) {
|
|
187
|
+
return [...new Set(this.copiesOwning(version)
|
|
188
|
+
.filter(copy => !copy.conditions.some(isPaperStretch))
|
|
189
|
+
.map(copy => copy.measurements.scale)
|
|
190
|
+
.filter((scale) => scale !== undefined && scale > 0))];
|
|
191
|
+
}
|
|
192
|
+
/** The copies of the version's own system that carry any of its symbols. */
|
|
193
|
+
copiesOwning(version) {
|
|
194
|
+
const system = systemIdOf(version.system);
|
|
195
|
+
const carrying = new Set(this.snapshot(version.id)
|
|
196
|
+
.flatMap(symbol => idsOf(symbol.carriers))
|
|
197
|
+
.flatMap(id => {
|
|
198
|
+
const copy = this.copyOf(id);
|
|
199
|
+
return copy ? [copy.id] : [];
|
|
200
|
+
}));
|
|
201
|
+
return this.edition.copies.filter(copy => carrying.has(copy.id) && systemIdOf(copy.production?.system) === system);
|
|
202
|
+
}
|
|
144
203
|
predecessorOf(versionId) {
|
|
145
204
|
const v = this.get(versionId);
|
|
146
205
|
if (!v?.basedOn)
|
|
147
206
|
return;
|
|
148
207
|
return this.get(idOf(v.basedOn));
|
|
149
208
|
}
|
|
150
|
-
|
|
209
|
+
/**
|
|
210
|
+
* Where along the roll the symbol lies, as its carriers put it, or
|
|
211
|
+
* nothing for a symbol no copy carries.
|
|
212
|
+
*
|
|
213
|
+
* Only the place is measured. Which track the symbol sits on is not
|
|
214
|
+
* a measurement but a question for a tracker bar, since a note of
|
|
215
|
+
* one pitch sits on exactly one position of a given bar: ask
|
|
216
|
+
* `bar.positionOf`. The carriers cannot answer it, because a copy
|
|
217
|
+
* cut for another system numbers its tracks differently, and one
|
|
218
|
+
* symbol may be carried by copies of both — averaging a red carrier
|
|
219
|
+
* on track 47 with a green one on 45 would give 46, a legal
|
|
220
|
+
* position a semitone away on either bar.
|
|
221
|
+
*/
|
|
222
|
+
placeOf(symbol) {
|
|
151
223
|
const carriers = this.getAll(idsOf(symbol.carriers));
|
|
152
|
-
if (carriers.length === 0)
|
|
224
|
+
if (carriers.length === 0)
|
|
153
225
|
return;
|
|
154
|
-
}
|
|
155
|
-
const farEnds = carriers.flatMap(carrier => carrier.vertical.to === undefined ? [] : [carrier.vertical.to]);
|
|
156
226
|
return {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
to: mean(carriers.map(carrier => carrier.horizontal.to))
|
|
161
|
-
},
|
|
162
|
-
vertical: {
|
|
163
|
-
unit: 'track',
|
|
164
|
-
from: mean(carriers.map(carrier => carrier.vertical.from)),
|
|
165
|
-
...(farEnds.length > 0 && { to: mean(farEnds) })
|
|
166
|
-
}
|
|
227
|
+
unit: 'mm',
|
|
228
|
+
from: mean(carriers.map(carrier => carrier.horizontal.from)),
|
|
229
|
+
to: mean(carriers.map(carrier => carrier.horizontal.to))
|
|
167
230
|
};
|
|
168
231
|
}
|
|
169
232
|
/** Where the symbol begins, as the mean onset of its carriers, or nothing for a symbol without a place. */
|
|
170
233
|
onsetOf(symbol) {
|
|
171
|
-
|
|
172
|
-
return carriers.length > 0 ? mean(carriers.map(carrier => carrier.horizontal.from)) : undefined;
|
|
234
|
+
return this.placeOf(symbol)?.from;
|
|
173
235
|
}
|
|
174
236
|
/** The symbols by onset, those without a place first; symbols at one place keep their order. */
|
|
175
237
|
inOrderOfPlace(symbols) {
|
|
@@ -231,13 +293,24 @@ export class EditionView {
|
|
|
231
293
|
}));
|
|
232
294
|
return withGen;
|
|
233
295
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
296
|
+
/**
|
|
297
|
+
* The symbol as a performance needs it: where it lies, and the
|
|
298
|
+
* position the performing bar reads it on.
|
|
299
|
+
*
|
|
300
|
+
* Nothing where that bar reads nothing of it, which is the case a
|
|
301
|
+
* transfer between systems leaves behind: a red `ForzandoOn` a
|
|
302
|
+
* green version still inherits cannot be performed on a green
|
|
303
|
+
* machine, and an edit has yet to say what took its place.
|
|
304
|
+
*/
|
|
305
|
+
simplifySymbol(symbol, bar) {
|
|
306
|
+
const horizontal = this.placeOf(symbol);
|
|
307
|
+
const position = bar.positionOf(symbol);
|
|
308
|
+
if (!horizontal || position === undefined)
|
|
237
309
|
return null;
|
|
238
310
|
return {
|
|
239
311
|
...symbol,
|
|
240
|
-
|
|
312
|
+
horizontal,
|
|
313
|
+
vertical: { unit: 'track', from: position }
|
|
241
314
|
};
|
|
242
315
|
}
|
|
243
316
|
}
|
package/lib/Emulation.js
CHANGED
|
@@ -154,7 +154,7 @@ export class Emulation {
|
|
|
154
154
|
this.negotiatedEvents =
|
|
155
155
|
view.snapshot(version.id)
|
|
156
156
|
.filter(isPerforation)
|
|
157
|
-
.map(symbol => view.simplifySymbol(symbol))
|
|
157
|
+
.map(symbol => view.simplifySymbol(symbol, this.system.trackerBar))
|
|
158
158
|
.filter(event => event !== null)
|
|
159
159
|
.filter(inScope);
|
|
160
160
|
if (this.negotiatedEvents.length === 0) {
|
package/lib/RollCopy.d.ts
CHANGED
|
@@ -301,17 +301,32 @@ export interface RollCopy extends WithType<'RollCopy'>, WithId {
|
|
|
301
301
|
*/
|
|
302
302
|
readFrom?: FeatureSource;
|
|
303
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* Whether the condition is the stretch or shrinkage of the paper. A
|
|
306
|
+
* scale put down to this belongs to the one exemplar and says nothing
|
|
307
|
+
* about the speed its system's rolls were cut at.
|
|
308
|
+
*/
|
|
309
|
+
export declare const isPaperStretch: (condition: RollConditionAssignment) => boolean;
|
|
310
|
+
/**
|
|
311
|
+
* The bar a copy is read by, which is the one it was cut for. A copy
|
|
312
|
+
* that names no system falls back to the T-100, as every copy was read
|
|
313
|
+
* before the systems were told apart; `reservationsAbout` says so.
|
|
314
|
+
*/
|
|
315
|
+
export declare const barOf: (copy: Pick<RollCopy, 'production'>) => TrackerBar;
|
|
304
316
|
/**
|
|
305
317
|
* Reads the features of a copy as the tracker bar would read them.
|
|
306
318
|
* Holes on a position the bar does not read carry no symbol and are
|
|
307
319
|
* dropped, which is what happens physically as well.
|
|
320
|
+
*
|
|
321
|
+
* The bar is named rather than defaulted: a copy is read by its own
|
|
322
|
+
* bar, and reading a green copy with the red one is a silent semitone.
|
|
308
323
|
*/
|
|
309
|
-
export declare function asSymbols(features: AnyFeature[], bar
|
|
324
|
+
export declare function asSymbols(features: AnyFeature[], bar: TrackerBar): AnySymbol[];
|
|
310
325
|
/**
|
|
311
326
|
* The tracks a copy carries holes on that the tracker bar does not read.
|
|
312
327
|
* A non-empty result usually means the scan is calibrated wrongly.
|
|
313
328
|
*/
|
|
314
|
-
export declare function unreadTracks(features: AnyFeature[], bar
|
|
329
|
+
export declare function unreadTracks(features: AnyFeature[], bar: TrackerBar): Map<Track, number>;
|
|
315
330
|
/**
|
|
316
331
|
* Falls back to the way the scan geometry was reconstructed before the
|
|
317
332
|
* calibration was recorded: the bass hard margin stood in for the phase
|
package/lib/RollCopy.js
CHANGED
|
@@ -1,17 +1,33 @@
|
|
|
1
1
|
import { v4 } from "uuid";
|
|
2
2
|
import { welteT100 } from "./systems/welteT100/bar";
|
|
3
|
+
import { trackerBarOf } from "./systems";
|
|
3
4
|
import { assignReference } from "./Assumption";
|
|
4
5
|
import { px, track } from "./Quantity";
|
|
5
6
|
export const rollConditions = [
|
|
6
7
|
'general',
|
|
7
8
|
'paper-stretch'
|
|
8
9
|
];
|
|
10
|
+
/**
|
|
11
|
+
* Whether the condition is the stretch or shrinkage of the paper. A
|
|
12
|
+
* scale put down to this belongs to the one exemplar and says nothing
|
|
13
|
+
* about the speed its system's rolls were cut at.
|
|
14
|
+
*/
|
|
15
|
+
export const isPaperStretch = (condition) => condition.conditionType === 'paper-stretch';
|
|
16
|
+
/**
|
|
17
|
+
* The bar a copy is read by, which is the one it was cut for. A copy
|
|
18
|
+
* that names no system falls back to the T-100, as every copy was read
|
|
19
|
+
* before the systems were told apart; `reservationsAbout` says so.
|
|
20
|
+
*/
|
|
21
|
+
export const barOf = (copy) => trackerBarOf(copy.production?.system) ?? welteT100;
|
|
9
22
|
/**
|
|
10
23
|
* Reads the features of a copy as the tracker bar would read them.
|
|
11
24
|
* Holes on a position the bar does not read carry no symbol and are
|
|
12
25
|
* dropped, which is what happens physically as well.
|
|
26
|
+
*
|
|
27
|
+
* The bar is named rather than defaulted: a copy is read by its own
|
|
28
|
+
* bar, and reading a green copy with the red one is a silent semitone.
|
|
13
29
|
*/
|
|
14
|
-
export function asSymbols(features, bar
|
|
30
|
+
export function asSymbols(features, bar) {
|
|
15
31
|
return features
|
|
16
32
|
.filter(feature => feature.type === 'Hole')
|
|
17
33
|
.flatMap((feature) => {
|
|
@@ -29,7 +45,7 @@ export function asSymbols(features, bar = welteT100) {
|
|
|
29
45
|
* The tracks a copy carries holes on that the tracker bar does not read.
|
|
30
46
|
* A non-empty result usually means the scan is calibrated wrongly.
|
|
31
47
|
*/
|
|
32
|
-
export function unreadTracks(features, bar
|
|
48
|
+
export function unreadTracks(features, bar) {
|
|
33
49
|
const counts = new Map();
|
|
34
50
|
features
|
|
35
51
|
.filter(feature => feature.type === 'Hole')
|
package/lib/TrackerBar.d.ts
CHANGED
|
@@ -58,11 +58,26 @@ export interface TrackerBar {
|
|
|
58
58
|
readonly paperSpeed?: SpeedMeasure;
|
|
59
59
|
/** `undefined` for a position the bar does not read. */
|
|
60
60
|
meaningOf(position: Track): TrackMeaning | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* `meaningOf` inverted: the position this bar reads the meaning on,
|
|
63
|
+
* or `undefined` where it does not read it at all. A symbol's track
|
|
64
|
+
* is this rather than anything measured, since a note of one pitch
|
|
65
|
+
* sits on exactly one position of a given bar.
|
|
66
|
+
*/
|
|
67
|
+
positionOf(meaning: TrackMeaning): Track | undefined;
|
|
61
68
|
/** `undefined` for a position the bar does not read. */
|
|
62
69
|
roleOf(position: Track): TrackRole | undefined;
|
|
63
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* What a position says, as a key. Two bars read the same thing exactly
|
|
73
|
+
* where their keys agree, which is what lets a symbol cross from one
|
|
74
|
+
* system to another and what decides whether two symbols collate.
|
|
75
|
+
*/
|
|
76
|
+
export declare const keyOf: (meaning: TrackMeaning) => string;
|
|
64
77
|
/** The roll system a tracker bar belongs to, as the roll metadata states it. */
|
|
65
78
|
export declare const systemOf: (bar: TrackerBar) => Concept;
|
|
79
|
+
/** The identifier of a system the type vocabulary knows, from the IRI naming it. */
|
|
80
|
+
export declare const systemIdIn: (id: string | undefined) => string | undefined;
|
|
66
81
|
/** The identifier of a system the type vocabulary knows, from its concept. */
|
|
67
82
|
export declare const systemIdOf: (system: Concept | undefined) => string | undefined;
|
|
68
83
|
/**
|
|
@@ -97,7 +112,7 @@ export declare const describeTrackerBar: (spec: TrackerBarSpec) => TrackerBar;
|
|
|
97
112
|
/**
|
|
98
113
|
* Puts a position of one bar onto the position of another that reads
|
|
99
114
|
* the same thing, or nowhere when the other bar does not read it. This
|
|
100
|
-
* is how a copy
|
|
101
|
-
*
|
|
115
|
+
* is how a copy read in one system's numbering is put into another's,
|
|
116
|
+
* as the migration does for a Licensee copy stored on T-100 tracks.
|
|
102
117
|
*/
|
|
103
118
|
export declare const translationBetween: (from: TrackerBar, to: TrackerBar) => (position: Track) => Track | undefined;
|
package/lib/TrackerBar.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { track } from "./Quantity";
|
|
2
|
+
/**
|
|
3
|
+
* What a position says, as a key. Two bars read the same thing exactly
|
|
4
|
+
* where their keys agree, which is what lets a symbol cross from one
|
|
5
|
+
* system to another and what decides whether two symbols collate.
|
|
6
|
+
*/
|
|
7
|
+
export const keyOf = (meaning) => meaning.type === 'note'
|
|
8
|
+
? `note ${meaning.pitch}`
|
|
9
|
+
: `expression ${meaning.scope} ${meaning.expressionType}`;
|
|
2
10
|
const SYSTEM_IRI = 'https://w3id.org/reo/type/system/';
|
|
3
11
|
/** The roll system a tracker bar belongs to, as the roll metadata states it. */
|
|
4
12
|
export const systemOf = (bar) => ({ id: SYSTEM_IRI + bar.id, name: bar.name, sameAs: [] });
|
|
13
|
+
/** The identifier of a system the type vocabulary knows, from the IRI naming it. */
|
|
14
|
+
export const systemIdIn = (id) => id?.startsWith(SYSTEM_IRI) ? id.slice(SYSTEM_IRI.length) : undefined;
|
|
5
15
|
/** The identifier of a system the type vocabulary knows, from its concept. */
|
|
6
|
-
export const systemIdOf = (system) => system?.id
|
|
16
|
+
export const systemIdOf = (system) => systemIdIn(system?.id);
|
|
7
17
|
const areasOf = ({ notes, trackCount }) => [
|
|
8
18
|
{ role: 'bass-expression', from: track(1), to: track(notes.from - 1) },
|
|
9
19
|
{ role: 'note', from: track(notes.from), to: track(notes.to) },
|
|
@@ -28,6 +38,11 @@ export const describeTrackerBar = (spec) => {
|
|
|
28
38
|
return undefined;
|
|
29
39
|
return { type: 'expression', expressionType, scope: scopeOf(role) };
|
|
30
40
|
};
|
|
41
|
+
const positions = new Map(Array.from({ length: spec.trackCount }, (_, i) => track(i + 1))
|
|
42
|
+
.flatMap(position => {
|
|
43
|
+
const meaning = meaningOf(position);
|
|
44
|
+
return meaning ? [[keyOf(meaning), position]] : [];
|
|
45
|
+
}));
|
|
31
46
|
const rewind = spec.rewindTrack
|
|
32
47
|
?? [...spec.expressions].find(([, type]) => type === 'Rewind')?.[0];
|
|
33
48
|
if (rewind === undefined) {
|
|
@@ -43,26 +58,17 @@ export const describeTrackerBar = (spec) => {
|
|
|
43
58
|
rewindTrack: track(rewind),
|
|
44
59
|
...(spec.paperSpeed && { paperSpeed: spec.paperSpeed }),
|
|
45
60
|
meaningOf,
|
|
61
|
+
positionOf: meaning => positions.get(keyOf(meaning)),
|
|
46
62
|
roleOf
|
|
47
63
|
};
|
|
48
64
|
};
|
|
49
|
-
const keyOf = (meaning) => meaning.type === 'note' ? `note ${meaning.pitch}` : `${meaning.scope} ${meaning.expressionType}`;
|
|
50
|
-
const positionsOf = (bar) => Array.from({ length: bar.trackCount }, (_, i) => track(i + 1));
|
|
51
65
|
/**
|
|
52
66
|
* Puts a position of one bar onto the position of another that reads
|
|
53
67
|
* the same thing, or nowhere when the other bar does not read it. This
|
|
54
|
-
* is how a copy
|
|
55
|
-
*
|
|
68
|
+
* is how a copy read in one system's numbering is put into another's,
|
|
69
|
+
* as the migration does for a Licensee copy stored on T-100 tracks.
|
|
56
70
|
*/
|
|
57
|
-
export const translationBetween = (from, to) => {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
const meaning = to.meaningOf(position);
|
|
61
|
-
if (meaning)
|
|
62
|
-
positions.set(keyOf(meaning), position);
|
|
63
|
-
});
|
|
64
|
-
return position => {
|
|
65
|
-
const meaning = from.meaningOf(position);
|
|
66
|
-
return meaning ? positions.get(keyOf(meaning)) : undefined;
|
|
67
|
-
};
|
|
71
|
+
export const translationBetween = (from, to) => (position) => {
|
|
72
|
+
const meaning = from.meaningOf(position);
|
|
73
|
+
return meaning && to.positionOf(meaning);
|
|
68
74
|
};
|
package/lib/Version.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Edit } from "./Edit";
|
|
2
|
-
import {
|
|
2
|
+
import { Concept } from "./Agent";
|
|
3
|
+
import { ActorAssignment, DateAssignment, ReferenceAssumption } from "./Assumption";
|
|
3
4
|
import { CollationTolerance } from "./Collation";
|
|
4
5
|
import { AnySymbol } from "./Symbol";
|
|
5
6
|
import { WithId, WithNote, WithType } from "./utils";
|
|
@@ -42,6 +43,37 @@ export type Derivation = ReferenceAssumption & {
|
|
|
42
43
|
};
|
|
43
44
|
/** The tolerance the derivation was collated at, or the default where it states none. */
|
|
44
45
|
export declare const collationToleranceOf: (derivation: Readonly<Derivation>) => CollationTolerance;
|
|
46
|
+
/**
|
|
47
|
+
* How a version was made, where that is known and worth stating.
|
|
48
|
+
*
|
|
49
|
+
* A roll issued for another system was re-punched by an editor of the
|
|
50
|
+
* publisher's, and that was an editorial act rather than a conversion:
|
|
51
|
+
* Lawson names Kähle as the man who corrected second masters for the
|
|
52
|
+
* green system. The rule he worked by is the procedure named here, and
|
|
53
|
+
* the version's edits carry it out, so the mechanical part of a
|
|
54
|
+
* transfer is stated once instead of being spelled out per note.
|
|
55
|
+
* @see lrmoo:F28 Expression Creation
|
|
56
|
+
*/
|
|
57
|
+
export interface VersionCreation {
|
|
58
|
+
/**
|
|
59
|
+
* Who carried the act out. An `ObjectAssumption`, so an attribution
|
|
60
|
+
* can carry the belief it rests on and the reasons for it.
|
|
61
|
+
* @see crm:P14 carried out by
|
|
62
|
+
*/
|
|
63
|
+
actor?: ActorAssignment;
|
|
64
|
+
/**
|
|
65
|
+
* When it took place.
|
|
66
|
+
* @see dcterms:date
|
|
67
|
+
*/
|
|
68
|
+
date?: DateAssignment;
|
|
69
|
+
/**
|
|
70
|
+
* The rule followed, as a term of the vocabulary: the notes stand
|
|
71
|
+
* at their pitch, the expression is re-spelled in the other
|
|
72
|
+
* system's words.
|
|
73
|
+
* @see crm:P33 used specific technique
|
|
74
|
+
*/
|
|
75
|
+
procedure?: Concept;
|
|
76
|
+
}
|
|
45
77
|
/**
|
|
46
78
|
* A version is defined by the sum of edits applied
|
|
47
79
|
* to the version it is based on. For simple identification,
|
|
@@ -54,6 +86,16 @@ export interface Version extends WithId, WithType<'Version'> {
|
|
|
54
86
|
* @see reo:siglum
|
|
55
87
|
*/
|
|
56
88
|
siglum: string;
|
|
89
|
+
/**
|
|
90
|
+
* The reproducing system this version is coded for. One roll was
|
|
91
|
+
* often issued for several of them, and a version is a reading in
|
|
92
|
+
* one system's words: its expression types are that system's
|
|
93
|
+
* vocabulary and its notes sit on that bar's positions. A system
|
|
94
|
+
* the type vocabulary knows carries the IRI of its concept as
|
|
95
|
+
* `id`, from which the export takes the system's own context.
|
|
96
|
+
* @see crm:P2 has type
|
|
97
|
+
*/
|
|
98
|
+
system: Concept;
|
|
57
99
|
/**
|
|
58
100
|
* Whether the version served as a master for reproductions
|
|
59
101
|
* or exists on one copy only.
|
|
@@ -65,6 +107,12 @@ export interface Version extends WithId, WithType<'Version'> {
|
|
|
65
107
|
* @see lrmoo:R76 is derivative of
|
|
66
108
|
*/
|
|
67
109
|
basedOn?: Derivation;
|
|
110
|
+
/**
|
|
111
|
+
* The act that made this version, where it is known: who carried it
|
|
112
|
+
* out, when, and by what rule.
|
|
113
|
+
* @see lrmoo:R17i was created by
|
|
114
|
+
*/
|
|
115
|
+
creation?: VersionCreation;
|
|
68
116
|
/**
|
|
69
117
|
* The list of edits that, applied to the base version, produce this version.
|
|
70
118
|
* @see reo:involvedEdit
|
package/lib/asJsonLd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { systemIdIn } from "./TrackerBar";
|
|
2
2
|
export const exportDate = (date) => {
|
|
3
3
|
const year = date.getFullYear();
|
|
4
4
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
@@ -39,20 +39,35 @@ const asJsonLdEntity = (obj) => {
|
|
|
39
39
|
return result;
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
* expression types as that system's terms.
|
|
42
|
+
* Gives every version the context of its own reproducing system, which
|
|
43
|
+
* reads its expression types as that system's terms.
|
|
44
|
+
*
|
|
45
|
+
* It sits on the version rather than on the edition because one edition
|
|
46
|
+
* may hold versions of several systems, and each system's context
|
|
47
|
+
* defines `expressionType` with its own `@vocab`. Two of them in one
|
|
48
|
+
* context array would leave every expression type in the document
|
|
49
|
+
* reading as whichever came last. An embedded context merges with the
|
|
50
|
+
* active one, so the shared prefixes survive.
|
|
44
51
|
*/
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
const withSystemContexts = (node) => {
|
|
53
|
+
if (Array.isArray(node))
|
|
54
|
+
return node.map(withSystemContexts);
|
|
55
|
+
if (node === null || typeof node !== 'object')
|
|
56
|
+
return node;
|
|
57
|
+
const walked = Object.fromEntries(Object.entries(node).map(([key, value]) => [key, withSystemContexts(value)]));
|
|
58
|
+
const system = node['@type'] === 'Version'
|
|
59
|
+
? systemIdIn(node.system?.['@id'])
|
|
60
|
+
: undefined;
|
|
61
|
+
return system
|
|
62
|
+
? { '@context': `https://w3id.org/reo/${system}/context.jsonld`, ...walked }
|
|
63
|
+
: walked;
|
|
48
64
|
};
|
|
49
65
|
export const asJsonLd = (edition) => {
|
|
50
66
|
// The context is the export's own; one carried in from an import must not override it.
|
|
51
|
-
const { base, copies, '@context': carried, ...rest } = asJsonLdEntity(edition);
|
|
67
|
+
const { base, copies, '@context': carried, ...rest } = withSystemContexts(asJsonLdEntity(edition));
|
|
52
68
|
return {
|
|
53
69
|
'@context': [
|
|
54
70
|
'https://w3id.org/reo/context.jsonld',
|
|
55
|
-
...systemContextOf(edition),
|
|
56
71
|
{
|
|
57
72
|
'@base': edition.base
|
|
58
73
|
}
|