linked-rolls 0.63.0 → 0.65.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/README.md +13 -5
- package/lib/analysis/constraints.d.ts +2 -2
- package/lib/analysis/constraints.js +38 -32
- package/lib/analysis/ownPaper.d.ts +3 -3
- package/lib/analysis/ownPaper.js +3 -3
- package/lib/analysis/reservations.d.ts +2 -2
- package/lib/analysis/reservations.js +6 -6
- package/lib/analysis/sigla.d.ts +4 -5
- package/lib/analysis/sigla.js +4 -4
- package/lib/analysis/stemma.d.ts +22 -0
- package/lib/analysis/stemma.js +38 -0
- package/lib/analysis/text.d.ts +31 -0
- package/lib/analysis/text.js +62 -0
- package/lib/analysis/witnesses.d.ts +5 -5
- package/lib/analysis/witnesses.js +27 -22
- package/lib/collation/alignment.js +55 -20
- package/lib/collation/scatter.d.ts +6 -6
- package/lib/collation/scatter.js +15 -13
- package/lib/emulation/Emulation.d.ts +7 -7
- package/lib/emulation/Emulation.js +29 -27
- package/lib/emulation/negotiation.d.ts +2 -2
- package/lib/emulation/negotiation.js +3 -2
- package/lib/index.d.ts +4 -1
- package/lib/index.js +4 -1
- package/lib/io/asJsonLd.js +43 -38
- package/lib/io/importJsonLd.js +11 -1
- package/lib/io/migrate.js +20 -26
- package/lib/lookup/lookup.d.ts +32 -0
- package/lib/lookup/lookup.js +40 -0
- package/lib/lookup/paths.d.ts +11 -0
- package/lib/lookup/paths.js +78 -0
- package/lib/lookup/perState.d.ts +32 -0
- package/lib/lookup/perState.js +63 -0
- package/lib/model/Edit.d.ts +4 -0
- package/lib/model/Edition.d.ts +0 -13
- package/lib/model/Version.d.ts +7 -1
- package/lib/ops/beliefs.d.ts +1 -1
- package/lib/ops/beliefs.js +1 -1
- package/lib/ops/collation.d.ts +3 -4
- package/lib/ops/collation.js +38 -32
- package/lib/ops/commands.d.ts +4 -5
- package/lib/ops/commands.js +10 -10
- package/lib/ops/copies.js +11 -3
- package/lib/ops/draft.d.ts +12 -12
- package/lib/ops/draft.js +11 -13
- package/lib/ops/edits.d.ts +1 -2
- package/lib/ops/edits.js +13 -10
- package/lib/ops/features.d.ts +2 -2
- package/lib/ops/features.js +3 -2
- package/lib/ops/immutable.d.ts +7 -1
- package/lib/ops/immutable.js +22 -8
- package/lib/ops/index.d.ts +1 -1
- package/lib/ops/index.js +6 -0
- package/lib/ops/versions.d.ts +2 -3
- package/lib/ops/versions.js +6 -5
- package/lib/readers/phillipsEroll.js +1 -8
- package/lib/readers/stanfordAton.js +2 -6
- package/lib/schema.json +2 -33
- package/lib/systems/welte.d.ts +17 -0
- package/lib/systems/welte.js +17 -1
- package/lib/systems/welteT100/system.d.ts +1 -16
- package/lib/systems/welteT100/system.js +3 -17
- package/lib/systems/welteT98/system.d.ts +1 -12
- package/lib/systems/welteT98/system.js +3 -13
- package/package.json +1 -1
- package/lib/view/EditionView.d.ts +0 -103
- package/lib/view/EditionView.js +0 -329
package/lib/ops/commands.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import { getAt } from "../
|
|
1
|
+
import { getAt, pathIn } from "../lookup/paths.js";
|
|
2
2
|
import { isCommand, placementRelations } from "../model/Symbol.js";
|
|
3
3
|
import { assignReference } from "../model/Assumption.js";
|
|
4
4
|
import { reading } from "./draft.js";
|
|
5
5
|
/**
|
|
6
|
-
* Runs the change on the command the
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Runs the change on the command with the id, in whichever version
|
|
7
|
+
* inserted it. A statement made there holds in every version that
|
|
8
|
+
* carries the command.
|
|
9
9
|
*/
|
|
10
|
-
const onCommand = (
|
|
11
|
-
const path =
|
|
10
|
+
const onCommand = (id, op) => reading(edition => draft => {
|
|
11
|
+
const path = pathIn(edition, id);
|
|
12
12
|
const symbol = path && getAt(path, draft);
|
|
13
13
|
if (isCommand(symbol))
|
|
14
14
|
op(symbol);
|
|
15
15
|
});
|
|
16
16
|
const clearPlacement = (command) => placementRelations.forEach(relation => { delete command[relation]; });
|
|
17
17
|
/** States how the follower is placed relative to the reference, in place of any earlier statement. */
|
|
18
|
-
export const placeCommand = (
|
|
18
|
+
export const placeCommand = (followerId, referenceId, relation) => onCommand(followerId, command => {
|
|
19
19
|
clearPlacement(command);
|
|
20
20
|
command[relation] = assignReference(referenceId);
|
|
21
21
|
});
|
|
22
|
-
export const unplaceCommand = (
|
|
22
|
+
export const unplaceCommand = (followerId) => onCommand(followerId, clearPlacement);
|
|
23
23
|
/** The pair is stated on `statingId` only, as the format asks. */
|
|
24
|
-
export const pairCommands = (
|
|
24
|
+
export const pairCommands = (statingId, partnerId) => onCommand(statingId, command => {
|
|
25
25
|
command.pairedWith = assignReference(partnerId);
|
|
26
26
|
});
|
|
27
|
-
export const unpairCommand = (
|
|
27
|
+
export const unpairCommand = (statingId) => onCommand(statingId, command => {
|
|
28
28
|
delete command.pairedWith;
|
|
29
29
|
});
|
package/lib/ops/copies.js
CHANGED
|
@@ -83,9 +83,17 @@ export const alignCopies = (date = new Date()) => draft => {
|
|
|
83
83
|
const own = draft.copies.find(c => c.id === reference.id);
|
|
84
84
|
if (own)
|
|
85
85
|
revertAlignment(own);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
// Aligning a copy moves neither the reference copy nor any other,
|
|
87
|
+
// so every copy is aligned from the one state. Read afresh after
|
|
88
|
+
// each, the draft would be copied with every copy aligned so far.
|
|
89
|
+
const state = stateOf(draft);
|
|
90
|
+
state.copies.forEach((copy, i) => {
|
|
91
|
+
if (copy.id === reference.id || featuresOf(copy).length === 0)
|
|
92
|
+
return;
|
|
93
|
+
const alignment = alignmentFor(state, copy, date);
|
|
94
|
+
if (alignment)
|
|
95
|
+
applyAlignment(alignment, draft.copies[i]);
|
|
96
|
+
});
|
|
89
97
|
};
|
|
90
98
|
/**
|
|
91
99
|
* Makes the copy the one whose millimetres are the edition's axis, and
|
package/lib/ops/draft.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { EditionView } from "../view/EditionView.js";
|
|
2
1
|
/** What the operations are built from: the type of an operation, how one finds what it writes to on the draft, and the edits it writes. */
|
|
3
2
|
import { Draft } from "immer";
|
|
4
3
|
import { Edition } from "../model/Edition.js";
|
|
@@ -12,10 +11,10 @@ import { WithId } from "../shared/utils.js";
|
|
|
12
11
|
/**
|
|
13
12
|
* A change to an edition, written onto an immer draft of it. One
|
|
14
13
|
* operation is one undo step, so an operation that has to read the
|
|
15
|
-
* edition
|
|
16
|
-
*
|
|
17
|
-
* Where operations run one after another on one draft,
|
|
18
|
-
*
|
|
14
|
+
* edition reads it as the draft stands when the operation is applied
|
|
15
|
+
* (`reading`) and does all its writing in the one function it builds
|
|
16
|
+
* from that. Where operations run one after another on one draft, each
|
|
17
|
+
* reads what the ones before it wrote.
|
|
19
18
|
*/
|
|
20
19
|
export type EditionOp = (draft: Draft<Edition>) => void;
|
|
21
20
|
export declare const noChange: EditionOp;
|
|
@@ -23,16 +22,17 @@ export declare const onCopy: (copyId: string, op: (copy: Draft<RollCopy>, draft:
|
|
|
23
22
|
export declare const onVersion: (versionId: string, op: (version: Draft<Version>, draft: Draft<Edition>) => void) => EditionOp;
|
|
24
23
|
export declare const onFeature: (copyId: string, featureId: string, op: (feature: Draft<FeatureOrPatch>) => void) => EditionOp;
|
|
25
24
|
/**
|
|
26
|
-
* The operation built from
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
25
|
+
* The operation built from the state it is applied to: the draft as it
|
|
26
|
+
* stands, an operation run before on the same draft included. A path
|
|
27
|
+
* or an index read off an earlier state would name what stood there
|
|
28
|
+
* before. What is worked out from a state is kept with it, so a draft
|
|
29
|
+
* nothing has changed yet is read from what is known already.
|
|
31
30
|
*/
|
|
32
|
-
export declare const reading: (
|
|
31
|
+
export declare const reading: (build: (edition: Edition) => EditionOp) => EditionOp;
|
|
33
32
|
/**
|
|
34
33
|
* The state a draft stands at, as plain data. Reading a draft proxies
|
|
35
|
-
* everything it touches, so what is only read is read from this
|
|
34
|
+
* everything it touches, so what is only read is read from this, and
|
|
35
|
+
* what is looked up in an edition is looked up in this (`takenOf`).
|
|
36
36
|
*/
|
|
37
37
|
export declare const stateOf: <T>(draft: Draft<T>) => T;
|
|
38
38
|
/** The ids of the items the rewriting left out. */
|
package/lib/ops/draft.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { EditionView } from "../view/EditionView.js";
|
|
2
1
|
/** What the operations are built from: the type of an operation, how one finds what it writes to on the draft, and the edits it writes. */
|
|
3
|
-
import {
|
|
2
|
+
import { isDraft } from "immer";
|
|
4
3
|
import { v4 } from "uuid";
|
|
5
4
|
import { unchecked, uncheckedMotivation } from "../collation/Collation.js";
|
|
6
5
|
import { insertedBy } from "../model/Version.js";
|
|
7
6
|
import { featuresOf } from "../model/RollCopy.js";
|
|
8
7
|
import { assignReference } from "../model/Assumption.js";
|
|
9
8
|
import { without, pruned } from "./immutable.js";
|
|
9
|
+
import { takenOf } from "../lookup/perState.js";
|
|
10
10
|
export const noChange = () => undefined;
|
|
11
11
|
export const onCopy = (copyId, op) => draft => {
|
|
12
12
|
const copy = draft.copies.find(c => c.id === copyId);
|
|
@@ -24,21 +24,19 @@ export const onFeature = (copyId, featureId, op) => onCopy(copyId, copy => {
|
|
|
24
24
|
op(feature);
|
|
25
25
|
});
|
|
26
26
|
/**
|
|
27
|
-
* The operation built from
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
27
|
+
* The operation built from the state it is applied to: the draft as it
|
|
28
|
+
* stands, an operation run before on the same draft included. A path
|
|
29
|
+
* or an index read off an earlier state would name what stood there
|
|
30
|
+
* before. What is worked out from a state is kept with it, so a draft
|
|
31
|
+
* nothing has changed yet is read from what is known already.
|
|
32
32
|
*/
|
|
33
|
-
export const reading = (
|
|
34
|
-
const state = stateOf(draft);
|
|
35
|
-
build(state === view.edition ? view : new EditionView(state))(draft);
|
|
36
|
-
};
|
|
33
|
+
export const reading = (build) => draft => build(stateOf(draft))(draft);
|
|
37
34
|
/**
|
|
38
35
|
* The state a draft stands at, as plain data. Reading a draft proxies
|
|
39
|
-
* everything it touches, so what is only read is read from this
|
|
36
|
+
* everything it touches, so what is only read is read from this, and
|
|
37
|
+
* what is looked up in an edition is looked up in this (`takenOf`).
|
|
40
38
|
*/
|
|
41
|
-
export const stateOf = (draft) => isDraft(draft) ?
|
|
39
|
+
export const stateOf = (draft) => isDraft(draft) ? takenOf(draft) : draft;
|
|
42
40
|
/** The ids of the items the rewriting left out. */
|
|
43
41
|
export const droppedIds = (before, after) => {
|
|
44
42
|
const kept = new Set(after.map(item => item.id));
|
package/lib/ops/edits.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { EditionView } from "../view/EditionView.js";
|
|
2
1
|
import { Edit } from "../model/Edit.js";
|
|
3
2
|
import { EditionOp } from "./draft.js";
|
|
4
3
|
/**
|
|
5
4
|
* Replaces the edits with a single one carrying all their insertions
|
|
6
5
|
* and deletions, classified by a guess at what the exchange does.
|
|
7
6
|
*/
|
|
8
|
-
export declare const mergeEdits: (
|
|
7
|
+
export declare const mergeEdits: (versionId: string, toMerge: readonly Edit[]) => EditionOp;
|
|
9
8
|
/** Replaces the edit with one edit per inserted and one per deleted symbol. */
|
|
10
9
|
export declare const splitEdit: (versionId: string, toSplit: Edit) => EditionOp;
|
package/lib/ops/edits.js
CHANGED
|
@@ -4,6 +4,9 @@ import { editsOf } from "../model/Version.js";
|
|
|
4
4
|
import { trackerBarOf } from "../systems/index.js";
|
|
5
5
|
import { distance, mm, subtract } from "../model/Quantity.js";
|
|
6
6
|
import { noChange, onVersion, insertion, deletion, reading } from "./draft.js";
|
|
7
|
+
import { predecessorOf } from "../analysis/stemma.js";
|
|
8
|
+
import { placeOf } from "../analysis/text.js";
|
|
9
|
+
import { symbolsIn, versionIn } from "../lookup/lookup.js";
|
|
7
10
|
const sameSequence = (a, b) => a.length === b.length && a.every((value, i) => value === b[i]);
|
|
8
11
|
const expressionTypesOf = (symbols) => symbols.filter((symbol) => symbol.type === 'expression').map(symbol => symbol.expressionType);
|
|
9
12
|
/** The spellings of a single added accent the bar offers, none where no bar is known. */
|
|
@@ -12,9 +15,9 @@ const lengthOf = (span) => subtract(span.to, span.from);
|
|
|
12
15
|
/** How far apart two onsets may lie for the one symbol to count as a replacement of the other. */
|
|
13
16
|
const REPLACEMENT_TOLERANCE = mm(5);
|
|
14
17
|
/** Shorten or prolong, where the inserted symbol starts about where the deleted one did. */
|
|
15
|
-
const replacementType = (
|
|
16
|
-
const after =
|
|
17
|
-
const before =
|
|
18
|
+
const replacementType = (edition, inserted, deleted) => {
|
|
19
|
+
const after = placeOf(edition, inserted);
|
|
20
|
+
const before = placeOf(edition, deleted);
|
|
18
21
|
if (!after || !before || distance(after.from, before.from) >= REPLACEMENT_TOLERANCE)
|
|
19
22
|
return undefined;
|
|
20
23
|
return lengthOf(after) < lengthOf(before) ? 'shorten' : 'prolong';
|
|
@@ -23,13 +26,13 @@ const replacementType = (view, inserted, deleted) => {
|
|
|
23
26
|
* A guess at what an edit does, from the symbols it exchanges and from
|
|
24
27
|
* the systems the version and the one it is based on are coded for.
|
|
25
28
|
*/
|
|
26
|
-
const guessEditType = (
|
|
29
|
+
const guessEditType = (edition, versionId, edit) => {
|
|
27
30
|
const inserts = edit.insert ?? [];
|
|
28
|
-
const deletes =
|
|
31
|
+
const deletes = symbolsIn(edition, edit.delete ?? []);
|
|
29
32
|
const inserted = expressionTypesOf(inserts);
|
|
30
33
|
const deleted = expressionTypesOf(deletes);
|
|
31
|
-
const bar = trackerBarOf(
|
|
32
|
-
const parentBar = trackerBarOf(
|
|
34
|
+
const bar = trackerBarOf(versionIn(edition, versionId)?.system);
|
|
35
|
+
const parentBar = trackerBarOf(predecessorOf(edition, versionId)?.system);
|
|
33
36
|
/**
|
|
34
37
|
* Where the version is coded for another system than its parent,
|
|
35
38
|
* expression matter changed is the transfer being carried out: a red
|
|
@@ -54,14 +57,14 @@ const guessEditType = (view, versionId, edit) => {
|
|
|
54
57
|
if (inserted.length === 0 && deleted.length === 1)
|
|
55
58
|
return 'remove-redundancy';
|
|
56
59
|
if (inserts.length === 1 && deletes.length === 1)
|
|
57
|
-
return replacementType(
|
|
60
|
+
return replacementType(edition, inserts[0], deletes[0]) ?? 'correct-error';
|
|
58
61
|
return 'correct-error';
|
|
59
62
|
};
|
|
60
63
|
/**
|
|
61
64
|
* Replaces the edits with a single one carrying all their insertions
|
|
62
65
|
* and deletions, classified by a guess at what the exchange does.
|
|
63
66
|
*/
|
|
64
|
-
export const mergeEdits = (
|
|
67
|
+
export const mergeEdits = (versionId, toMerge) => reading(edition => {
|
|
65
68
|
if (toMerge.length === 0)
|
|
66
69
|
return noChange;
|
|
67
70
|
const merged = {
|
|
@@ -70,7 +73,7 @@ export const mergeEdits = (given, versionId, toMerge) => reading(given, view =>
|
|
|
70
73
|
insert: toMerge.flatMap(edit => edit.insert ?? []),
|
|
71
74
|
delete: toMerge.flatMap(edit => edit.delete ?? [])
|
|
72
75
|
};
|
|
73
|
-
merged.editType = guessEditType(
|
|
76
|
+
merged.editType = guessEditType(edition, versionId, merged);
|
|
74
77
|
const mergedIds = new Set(toMerge.map(edit => edit.id));
|
|
75
78
|
return onVersion(versionId, version => {
|
|
76
79
|
version.edits = [...editsOf(version).filter(edit => !mergedIds.has(edit.id)), merged];
|
package/lib/ops/features.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Edition } from "../model/Edition.js";
|
|
2
2
|
import { ModificationPurpose } from "../model/RollCopy.js";
|
|
3
3
|
import { FeatureConditionAssignment, FeatureOrPatch, NestedFeature } from "../model/Feature.js";
|
|
4
4
|
import { EditionOp } from "./draft.js";
|
|
@@ -62,7 +62,7 @@ export declare const mergeObstacle: (features: readonly FeatureOrPatch[]) => Mer
|
|
|
62
62
|
* feature is the work of one act, and where the features stand is the
|
|
63
63
|
* edition's business rather than theirs.
|
|
64
64
|
*/
|
|
65
|
-
export declare const mergeObstacleIn: (
|
|
65
|
+
export declare const mergeObstacleIn: (edition: Edition, featureIds: readonly string[]) => MergeObstacle | undefined;
|
|
66
66
|
/**
|
|
67
67
|
* Replaces the features of the copy with a single one covering them
|
|
68
68
|
* all, where a scan has split what the editor reads as one feature.
|
package/lib/ops/features.js
CHANGED
|
@@ -6,6 +6,7 @@ import { mm } from "../model/Quantity.js";
|
|
|
6
6
|
import { onCopy, onFeature, stateOf } from "./draft.js";
|
|
7
7
|
import { without, mapped, pruned, replacing, isRecord } from "./immutable.js";
|
|
8
8
|
import { renameReferences, forgetFeatures } from "./forget.js";
|
|
9
|
+
import { copyOfFeature } from "../lookup/lookup.js";
|
|
9
10
|
/**
|
|
10
11
|
* Whether the feature's own kind allows a condition of this kind. The
|
|
11
12
|
* type narrowed to is the weaker statement, holding of a condition any
|
|
@@ -209,8 +210,8 @@ const obstacleIn = (copy, featureIds) => {
|
|
|
209
210
|
* feature is the work of one act, and where the features stand is the
|
|
210
211
|
* edition's business rather than theirs.
|
|
211
212
|
*/
|
|
212
|
-
export const mergeObstacleIn = (
|
|
213
|
-
const copy = featureIds.map(id =>
|
|
213
|
+
export const mergeObstacleIn = (edition, featureIds) => {
|
|
214
|
+
const copy = featureIds.map(id => copyOfFeature(edition, id)).find(copy => copy !== undefined);
|
|
214
215
|
return copy ? obstacleIn(copy, featureIds) : 'fewer-than-two';
|
|
215
216
|
};
|
|
216
217
|
/** The items with the replacement in the place of the first it stands for, the others dropped. */
|
package/lib/ops/immutable.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/** Rewritings that return the very object they were given wherever they change nothing, so that an immer draft is left untouched there. */
|
|
2
2
|
/** The items that do not match, or the very same array when none does, so that a draft stays untouched. */
|
|
3
3
|
export declare const without: <T>(items: T[], matches: (item: T) => boolean) => T[];
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* The items each changed, or the very same array when the change left
|
|
6
|
+
* every one as it was. The array is copied at the first item that
|
|
7
|
+
* changes, so that one left as it was costs no copy.
|
|
8
|
+
*/
|
|
5
9
|
export declare const mapped: <T>(items: T[], change: (item: T) => T) => T[];
|
|
6
10
|
/** The items each changed, less those the change emptied; the very same array where it changed none. */
|
|
7
11
|
export declare const pruned: <T>(items: T[], change: (item: T) => T, emptied: (item: T) => boolean) => T[];
|
|
@@ -10,6 +14,8 @@ export declare const replacing: <T extends object, K extends keyof T>(record: T,
|
|
|
10
14
|
/** A record of the edition, as the walk over it sees one. */
|
|
11
15
|
export type Node = Record<string, unknown>;
|
|
12
16
|
export declare const isRecord: (value: unknown) => value is Node;
|
|
17
|
+
/** The record's values each changed, or the very same record where the change left every one as it was, copied as `mapped` copies. */
|
|
18
|
+
export declare const withValues: (record: Node, change: (value: unknown) => unknown) => Node;
|
|
13
19
|
/** The value with the change applied to every record within it and then to itself, innermost first. */
|
|
14
20
|
export declare const deeply: (change: (record: object) => object) => (value: unknown) => unknown;
|
|
15
21
|
/** Writes onto the draft what the rewriting changed, as deep as the change reaches. */
|
package/lib/ops/immutable.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
/** Rewritings that return the very object they were given wherever they change nothing, so that an immer draft is left untouched there. */
|
|
2
2
|
/** The items that do not match, or the very same array when none does, so that a draft stays untouched. */
|
|
3
3
|
export const without = (items, matches) => items.some(matches) ? items.filter(item => !matches(item)) : items;
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* The items each changed, or the very same array when the change left
|
|
6
|
+
* every one as it was. The array is copied at the first item that
|
|
7
|
+
* changes, so that one left as it was costs no copy.
|
|
8
|
+
*/
|
|
5
9
|
export const mapped = (items, change) => {
|
|
6
|
-
|
|
7
|
-
|
|
10
|
+
let changed;
|
|
11
|
+
items.forEach((item, i) => {
|
|
12
|
+
const after = change(item);
|
|
13
|
+
if (after !== item)
|
|
14
|
+
(changed ?? (changed = items.slice()))[i] = after;
|
|
15
|
+
});
|
|
16
|
+
return changed ?? items;
|
|
8
17
|
};
|
|
9
18
|
/** The items each changed, less those the change emptied; the very same array where it changed none. */
|
|
10
19
|
export const pruned = (items, change, emptied) => {
|
|
@@ -14,11 +23,16 @@ export const pruned = (items, change, emptied) => {
|
|
|
14
23
|
/** The record with the field replaced, or the very same record where that is what stood there. */
|
|
15
24
|
export const replacing = (record, key, value) => record[key] === value ? record : { ...record, [key]: value };
|
|
16
25
|
export const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
17
|
-
/** The record's values each changed, or the very same record where the change left every one as it was. */
|
|
18
|
-
const withValues = (record, change) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
/** The record's values each changed, or the very same record where the change left every one as it was, copied as `mapped` copies. */
|
|
27
|
+
export const withValues = (record, change) => {
|
|
28
|
+
let changed;
|
|
29
|
+
Object.keys(record).forEach(key => {
|
|
30
|
+
const value = record[key];
|
|
31
|
+
const after = change(value);
|
|
32
|
+
if (after !== value)
|
|
33
|
+
(changed ?? (changed = { ...record }))[key] = after;
|
|
34
|
+
});
|
|
35
|
+
return changed ?? record;
|
|
22
36
|
};
|
|
23
37
|
/** The value with the change applied to every record within it and then to itself, innermost first. */
|
|
24
38
|
export const deeply = (change) => {
|
package/lib/ops/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* They are grouped by what they change under `ops/`; this module names
|
|
4
4
|
* the ones the library offers.
|
|
5
5
|
*/
|
|
6
|
-
export type
|
|
6
|
+
export { stateOf, type EditionOp } from "./draft.js";
|
|
7
7
|
export { createVersion, addCopy, alignmentFor, alignCopy, alignCopies, chooseReferenceCopy, unalignCopy, shortenCopy, unshortenCopy, stateSource, clearSource, nameCopy, stateCarriage, clearCarriage, addGeneralCondition, statePaperStretch, addTear, symbolsCarriedOnlyBy, removeCopy } from "./copies.js";
|
|
8
8
|
export { stateFeatureCondition, addFeature, addBorneFeature, removeFeatures, mergeObstacle, mergeObstacleIn, mergeFeatures, type FeatureAct, type MergeObstacle } from "./features.js";
|
|
9
9
|
export { detachVersion, removeVersion, removeSymbols, deriveVersion, stateDerivation, clearDerivation } from "./versions.js";
|
package/lib/ops/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The operations on an edition, each written onto an immer draft of it.
|
|
3
|
+
* They are grouped by what they change under `ops/`; this module names
|
|
4
|
+
* the ones the library offers.
|
|
5
|
+
*/
|
|
6
|
+
export { stateOf } from "./draft.js";
|
|
1
7
|
export { createVersion, addCopy, alignmentFor, alignCopy, alignCopies, chooseReferenceCopy, unalignCopy, shortenCopy, unshortenCopy, stateSource, clearSource, nameCopy, stateCarriage, clearCarriage, addGeneralCondition, statePaperStretch, addTear, symbolsCarriedOnlyBy, removeCopy } from "./copies.js";
|
|
2
8
|
export { stateFeatureCondition, addFeature, addBorneFeature, removeFeatures, mergeObstacle, mergeObstacleIn, mergeFeatures } from "./features.js";
|
|
3
9
|
export { detachVersion, removeVersion, removeSymbols, deriveVersion, stateDerivation, clearDerivation } from "./versions.js";
|
package/lib/ops/versions.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { EditionView } from "../view/EditionView.js";
|
|
2
1
|
import { Derivation, Version } from "../model/Version.js";
|
|
3
2
|
import { Belief } from "../model/Assumption.js";
|
|
4
3
|
import { EditionOp } from "./draft.js";
|
|
@@ -9,13 +8,13 @@ export declare const hypothesesBeside: (version: Readonly<Version>, parentId: st
|
|
|
9
8
|
* own insertions, and its derivations go, the hypotheses among them,
|
|
10
9
|
* with the motivations that belonged to them.
|
|
11
10
|
*/
|
|
12
|
-
export declare const detachVersion: (
|
|
11
|
+
export declare const detachVersion: (versionId: string) => EditionOp;
|
|
13
12
|
/**
|
|
14
13
|
* Takes the version out. Whatever read its text against it comes to
|
|
15
14
|
* stand on its own, a hypothesis that something derives from it goes,
|
|
16
15
|
* and so does a copy's statement that it carries the version.
|
|
17
16
|
*/
|
|
18
|
-
export declare const removeVersion: (
|
|
17
|
+
export declare const removeVersion: (versionId: string) => EditionOp;
|
|
19
18
|
/** Takes the symbols out of the version's own insertions, and the edits that had nothing else. */
|
|
20
19
|
export declare const removeSymbols: (versionId: string, symbolIds: readonly string[]) => EditionOp;
|
|
21
20
|
/** Moves the edits into a new version based on this one. */
|
package/lib/ops/versions.js
CHANGED
|
@@ -4,6 +4,7 @@ import { assignReference, idOf } from "../model/Assumption.js";
|
|
|
4
4
|
import { onVersion, stateOf, insertion, dropInsertions, referenceHeld, withoutReferences, reading } from "./draft.js";
|
|
5
5
|
import { without } from "./immutable.js";
|
|
6
6
|
import { dropStatements } from "./copies.js";
|
|
7
|
+
import { snapshotOf } from "../analysis/text.js";
|
|
7
8
|
/** Whether the version's text is read against the parent, its principal derivation naming it. */
|
|
8
9
|
const readsAgainst = (version, parentId) => {
|
|
9
10
|
const principal = principalDerivationOf(version);
|
|
@@ -30,8 +31,8 @@ const dropDerivations = (version, matches) => {
|
|
|
30
31
|
* own insertions, and its derivations go, the hypotheses among them,
|
|
31
32
|
* with the motivations that belonged to them.
|
|
32
33
|
*/
|
|
33
|
-
export const detachVersion = (
|
|
34
|
-
const edits =
|
|
34
|
+
export const detachVersion = (versionId) => reading(edition => {
|
|
35
|
+
const edits = snapshotOf(edition, versionId).map(insertion);
|
|
35
36
|
return onVersion(versionId, version => {
|
|
36
37
|
version.edits = edits;
|
|
37
38
|
delete version.basedOn;
|
|
@@ -43,10 +44,10 @@ export const detachVersion = (given, versionId) => reading(given, view => {
|
|
|
43
44
|
* stand on its own, a hypothesis that something derives from it goes,
|
|
44
45
|
* and so does a copy's statement that it carries the version.
|
|
45
46
|
*/
|
|
46
|
-
export const removeVersion = (
|
|
47
|
-
const detachments =
|
|
47
|
+
export const removeVersion = (versionId) => reading(edition => {
|
|
48
|
+
const detachments = edition.versions
|
|
48
49
|
.filter(version => readsAgainst(version, versionId))
|
|
49
|
-
.map(version => detachVersion(
|
|
50
|
+
.map(version => detachVersion(version.id));
|
|
50
51
|
const namesIt = (reference) => idOf(reference) === versionId;
|
|
51
52
|
return draft => {
|
|
52
53
|
detachments.forEach(detach => detach(draft));
|
|
@@ -129,12 +129,6 @@ const controlOffsetOf = (system, given) => {
|
|
|
129
129
|
}
|
|
130
130
|
return measured;
|
|
131
131
|
};
|
|
132
|
-
/** The position each note of the bar sounds from. */
|
|
133
|
-
const positionsByPitch = (bar) => new Map(Array.from({ length: bar.trackCount }, (_, index) => track(index + 1))
|
|
134
|
-
.flatMap((position) => {
|
|
135
|
-
const meaning = bar.meaningOf(position);
|
|
136
|
-
return meaning?.type === 'note' ? [[meaning.pitch, position]] : [];
|
|
137
|
-
}));
|
|
138
132
|
/**
|
|
139
133
|
* Reads one of his e-roll files as a copy of the roll, in millimetres
|
|
140
134
|
* of paper, on the bar it was cut for and in that bar's numbering. A
|
|
@@ -146,8 +140,7 @@ export function readFromPhillipsEroll(buffer, { system = welteT100, controlOffse
|
|
|
146
140
|
const elapsedAt = clockOf(temposIn(timed), file.header.ticksPerBeat);
|
|
147
141
|
const place = placeAt ?? atStatedSpeed(system);
|
|
148
142
|
const offset = controlOffsetOf(system, controlOffset);
|
|
149
|
-
const
|
|
150
|
-
const positionOf = (number) => notePositions.get(number) ?? track(number - offset);
|
|
143
|
+
const positionOf = (number) => system.positionOf({ type: 'note', pitch: number }) ?? track(number - offset);
|
|
151
144
|
const features = notesIn(timed).flatMap((note) => {
|
|
152
145
|
const position = positionOf(note.pitch);
|
|
153
146
|
if (!system.meaningOf(position))
|
|
@@ -3,6 +3,7 @@ import { AtonParser } from "./AtonParser.js";
|
|
|
3
3
|
import { systemOf } from "../systems/TrackerBar.js";
|
|
4
4
|
import { welteT100 } from "../systems/welteT100/bar.js";
|
|
5
5
|
import { inMillimeters, mean, mm, pixelsPerInch, px, subtract, track } from "../model/Quantity.js";
|
|
6
|
+
import { medianOf } from "../collation/statistics.js";
|
|
6
7
|
/** Values in these files carry their unit as a suffix, e.g. "37.7646px". */
|
|
7
8
|
const readPx = (value) => px(parseFloat(value));
|
|
8
9
|
/** The parser writes one record as an object and several as an array. */
|
|
@@ -20,11 +21,6 @@ const chainedBadHoles = (holes, calibration) => holes
|
|
|
20
21
|
...hole,
|
|
21
22
|
TRACKER_HOLE: `${Math.round((readPx(hole.CENTROID_COL) - calibration.offset) / calibration.separation)}`
|
|
22
23
|
}));
|
|
23
|
-
const median = (values) => {
|
|
24
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
25
|
-
const middle = Math.floor(sorted.length / 2);
|
|
26
|
-
return sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
|
|
27
|
-
};
|
|
28
24
|
const mostFrequent = (values) => {
|
|
29
25
|
const counts = values.reduce((acc, value) => acc.set(value, (acc.get(value) || 0) + 1), new Map());
|
|
30
26
|
return [...counts].sort(([, a], [, b]) => b - a)[0]?.[0];
|
|
@@ -76,7 +72,7 @@ const calibrationShiftIn = (holes, bar) => {
|
|
|
76
72
|
const gridOffsetOf = (holes, separation, stated) => {
|
|
77
73
|
if (stated !== undefined)
|
|
78
74
|
return readPx(stated);
|
|
79
|
-
return
|
|
75
|
+
return medianOf(holes.map(hole => px(readPx(hole.CENTROID_COL) - +hole.TRACKER_HOLE * separation))) ?? px(0);
|
|
80
76
|
};
|
|
81
77
|
const chainsAmong = (holes) => holes
|
|
82
78
|
.filter(hole => hole.NOTE_ATTACK && hole.OFF_TIME)
|
package/lib/schema.json
CHANGED
|
@@ -272,32 +272,6 @@
|
|
|
272
272
|
],
|
|
273
273
|
"type": "string"
|
|
274
274
|
},
|
|
275
|
-
"CollationTolerance": {
|
|
276
|
-
"description": "The window two readings of one feature must fall in to be collated: how far apart they may lie at either end, and where that window is centred.\n\nThe centre is worth stating. Two copies of one roll differ by a systematic offset as well as by scatter, and a window centred on nothing has to be widened by the whole of that offset before it admits what the offset alone displaces. Naming the offset lets the tolerance stand for the scatter only. A window that names none is centred on nothing, which is what every window written before the offset was held here means.",
|
|
277
|
-
"properties": {
|
|
278
|
-
"offsetEnd": {
|
|
279
|
-
"$ref": "#/definitions/Millimeters",
|
|
280
|
-
"description": "The same at the end of a feature, in the same direction."
|
|
281
|
-
},
|
|
282
|
-
"offsetStart": {
|
|
283
|
-
"$ref": "#/definitions/Millimeters",
|
|
284
|
-
"description": "How much later the derived version puts the start of a feature than the version it is read against, where the two differ systematically. Nothing where they do not.\n\nThe direction is the one a collation measures in, the child less the parent, and it is not symmetric: stored the other way round the window sits on the wrong side of the readings and merges what it should separate. A measurement taken over the parent's copies gives the negative of this, which is what `toleranceAcross` asks the side for."
|
|
285
|
-
},
|
|
286
|
-
"toleranceEnd": {
|
|
287
|
-
"$ref": "#/definitions/Millimeters",
|
|
288
|
-
"description": "How far they may lie apart at the end, measured from `offsetEnd`."
|
|
289
|
-
},
|
|
290
|
-
"toleranceStart": {
|
|
291
|
-
"$ref": "#/definitions/Millimeters",
|
|
292
|
-
"description": "How far the two readings may lie apart at the start of a feature, measured from `offsetStart`."
|
|
293
|
-
}
|
|
294
|
-
},
|
|
295
|
-
"required": [
|
|
296
|
-
"toleranceStart",
|
|
297
|
-
"toleranceEnd"
|
|
298
|
-
],
|
|
299
|
-
"type": "object"
|
|
300
|
-
},
|
|
301
275
|
"Concept": {
|
|
302
276
|
"anyOf": [
|
|
303
277
|
{
|
|
@@ -523,7 +497,7 @@
|
|
|
523
497
|
"type": "string"
|
|
524
498
|
},
|
|
525
499
|
"Edit": {
|
|
526
|
-
"description": "A set of edits transforms a version of a roll into another version. Edits insert or delete symbols, or both (= replace). Edits may be motivated by a given set of reasons, e.g. to add an additional accent or to correct an error. If an edit is the interpretation of a metamark, such as a pencil mark, this should be made explicit using a meaning comprehension on the `@annotation` field.",
|
|
500
|
+
"description": "A set of edits transforms a version of a roll into another version. Edits insert or delete symbols, or both (= replace). Edits may be motivated by a given set of reasons, e.g. to add an additional accent or to correct an error. If an edit is the interpretation of a metamark, such as a pencil mark, this should be made explicit using a meaning comprehension on the `@annotation` field. An edit states no date. That it ended no later than the act that made a feature carrying a symbol it inserts, a reasoner derives with the axioms of reo.ttl; which of those dates bounds it closest is for a query to pick.",
|
|
527
501
|
"properties": {
|
|
528
502
|
"@annotation": {
|
|
529
503
|
"description": "An optional annotation expressing a belief about this assumption. Uses the JSON-LD-star `@annotation` mechanism to attach epistemic metadata (certainty and reasons) to any triple.",
|
|
@@ -671,11 +645,6 @@
|
|
|
671
645
|
"EditionCreation": {
|
|
672
646
|
"description": "This type describes the creation of an edition, i.e. the editor, publisher, and publication date.",
|
|
673
647
|
"properties": {
|
|
674
|
-
"collationTolerance": {
|
|
675
|
-
"$ref": "#/definitions/CollationTolerance",
|
|
676
|
-
"deprecated": true,
|
|
677
|
-
"description": "The tolerance every version of the edition was collated at, before each derivation stated its own. How precisely two copies place a symbol depends on which two they are, so the tolerance belongs to the derivation, `Derivation.collationTolerance`, and migration writes this one onto every derivation that gives none. Nothing else reads it. A window's offset is the displacement between two particular copies and so can never be stated here. Not exported to RDF."
|
|
678
|
-
},
|
|
679
648
|
"editors": {
|
|
680
649
|
"description": "The persons who prepared the edition, each with the part they took in the editorial work. An edition written before this field existed carries none.",
|
|
681
650
|
"items": {
|
|
@@ -3523,7 +3492,7 @@
|
|
|
3523
3492
|
},
|
|
3524
3493
|
"creation": {
|
|
3525
3494
|
"$ref": "#/definitions/VersionCreation",
|
|
3526
|
-
"description": "The act that made this version, where it is known: who carried it out, when, and by what rule.",
|
|
3495
|
+
"description": "The act that made this version, where it is known: who carried it out, when, and by what rule. The export states the act for every version, stating nothing of it where nothing is known, so that the axioms of reo.ttl can place it in time: it started after the recording and the creation of every version it derives from had started, and ended no later than the punching of any copy carrying a symbol it added. A date stated here stands beside those; which of them bounds the act closest is for a query to pick.",
|
|
3527
3496
|
"ontology": "lrmoo:R17i was created by"
|
|
3528
3497
|
},
|
|
3529
3498
|
"edits": {
|
package/lib/systems/welte.d.ts
CHANGED
|
@@ -42,6 +42,23 @@ export type WelteOptions = {
|
|
|
42
42
|
};
|
|
43
43
|
/** Whether two sets of constants are the same, value for value. */
|
|
44
44
|
export declare const sameParameters: (a: Parameters, b: Parameters) => boolean;
|
|
45
|
+
/**
|
|
46
|
+
* The two readings of the pedal mechanism the emulator offers, by name,
|
|
47
|
+
* which the systems share: Hagmann has the valves and bellows that carry
|
|
48
|
+
* out the movements "in beiden Systemen dieselbe" (p. 106), and only the
|
|
49
|
+
* Vorpneumatik differs. Under `damping` the dampers reach the strings
|
|
50
|
+
* within the shortest lift the rolls punch, so every lift damps. Under
|
|
51
|
+
* `brushing` their fall is slowed until the quick runs of latch changes
|
|
52
|
+
* in the SUPRA corpus dip without damping, at the price that lifts
|
|
53
|
+
* shorter than about 265 ms brush as well.
|
|
54
|
+
*/
|
|
55
|
+
export declare const pedalPresets: {
|
|
56
|
+
damping: Readonly<Record<string, number>>;
|
|
57
|
+
brushing: Readonly<Record<string, number>>;
|
|
58
|
+
};
|
|
59
|
+
export type PedalPreset = keyof typeof pedalPresets;
|
|
60
|
+
/** The preset a set of pedal constants is, if it is one. */
|
|
61
|
+
export declare const pedalPresetOf: (pedals: Parameters) => PedalPreset | undefined;
|
|
45
62
|
/**
|
|
46
63
|
* Between the edition's shared place axis and this version's own paper.
|
|
47
64
|
* `toOwnPaper` is 1 for a version on the axis it was measured on, and about
|
package/lib/systems/welte.js
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
|
-
import { clamp, geometryInMm, Grid, levelChanges, mezzoforteTravel, paperSeconds, ROWS_PER_MM, travelBetweenRails, } from "welte-mignon-emulator";
|
|
1
|
+
import { clamp, geometryInMm, Grid, levelChanges, mezzoforteTravel, paperSeconds, pedalBrushing, pedalDefaults, ROWS_PER_MM, travelBetweenRails, } from "welte-mignon-emulator";
|
|
2
2
|
import { inCentimeters, mm, seconds } from "../model/Quantity.js";
|
|
3
3
|
import { partitionPoint } from "../shared/sorted.js";
|
|
4
4
|
import { velocityOf } from "./velocity.js";
|
|
5
5
|
/** Whether two sets of constants are the same, value for value. */
|
|
6
6
|
export const sameParameters = (a, b) => Object.keys(a).length === Object.keys(b).length
|
|
7
7
|
&& Object.entries(a).every(([name, value]) => b[name] === value);
|
|
8
|
+
/**
|
|
9
|
+
* The two readings of the pedal mechanism the emulator offers, by name,
|
|
10
|
+
* which the systems share: Hagmann has the valves and bellows that carry
|
|
11
|
+
* out the movements "in beiden Systemen dieselbe" (p. 106), and only the
|
|
12
|
+
* Vorpneumatik differs. Under `damping` the dampers reach the strings
|
|
13
|
+
* within the shortest lift the rolls punch, so every lift damps. Under
|
|
14
|
+
* `brushing` their fall is slowed until the quick runs of latch changes
|
|
15
|
+
* in the SUPRA corpus dip without damping, at the price that lifts
|
|
16
|
+
* shorter than about 265 ms brush as well.
|
|
17
|
+
*/
|
|
18
|
+
export const pedalPresets = {
|
|
19
|
+
damping: pedalDefaults,
|
|
20
|
+
brushing: pedalBrushing
|
|
21
|
+
};
|
|
22
|
+
/** The preset a set of pedal constants is, if it is one. */
|
|
23
|
+
export const pedalPresetOf = (pedals) => Object.keys(pedalPresets).find(name => sameParameters(pedalPresets[name], pedals));
|
|
8
24
|
/** Paper the grid runs on past the last hole, so that a final pedal release completes. */
|
|
9
25
|
const RUN_OUT = mm(100);
|
|
10
26
|
const isNote = (event) => event.type === 'note';
|
|
@@ -3,7 +3,7 @@ import { Instrument, RollNumber } from "welte-mignon-emulator/t100";
|
|
|
3
3
|
import { NegotiatedEvent, Performance, ReproducingSystem, RollProperties } from "../ReproducingSystem.js";
|
|
4
4
|
import { type WelteOptions } from "../welte.js";
|
|
5
5
|
export type { VelocityMap } from "../velocity.js";
|
|
6
|
-
export { secondsAt } from "../welte.js";
|
|
6
|
+
export { pedalPresetOf, pedalPresets, secondsAt, type PedalPreset } from "../welte.js";
|
|
7
7
|
export type { Instrument } from "welte-mignon-emulator/t100";
|
|
8
8
|
export type InstrumentName = 'consensus' | RollNumber;
|
|
9
9
|
/**
|
|
@@ -19,21 +19,6 @@ export declare const instrumentNames: readonly InstrumentName[];
|
|
|
19
19
|
export declare const nuanceOf: (instrument: Instrument) => Record<Half, Parameters>;
|
|
20
20
|
/** The instrument a pair of nuancing constants belongs to, if it is one. */
|
|
21
21
|
export declare const instrumentNameOf: (nuance: Record<Half, Parameters>) => InstrumentName | undefined;
|
|
22
|
-
/**
|
|
23
|
-
* The two readings of the pedal mechanism the emulator offers, by name.
|
|
24
|
-
* Under `damping` the dampers reach the strings within the shortest lift
|
|
25
|
-
* the rolls punch, so every lift damps. Under `brushing` their fall is
|
|
26
|
-
* slowed until the quick runs of latch changes in the SUPRA corpus dip
|
|
27
|
-
* without damping, at the price that lifts shorter than about 265 ms brush
|
|
28
|
-
* as well.
|
|
29
|
-
*/
|
|
30
|
-
export declare const pedalPresets: {
|
|
31
|
-
damping: Readonly<Record<string, number>>;
|
|
32
|
-
brushing: Readonly<Record<string, number>>;
|
|
33
|
-
};
|
|
34
|
-
export type PedalPreset = keyof typeof pedalPresets;
|
|
35
|
-
/** The preset a set of pedal constants is, if it is one. */
|
|
36
|
-
export declare const pedalPresetOf: (pedals: Parameters) => PedalPreset | undefined;
|
|
37
22
|
/** The T-100 takes the options every Welte system does; see `WelteOptions`. */
|
|
38
23
|
export type WelteT100Options = WelteOptions;
|
|
39
24
|
export declare const defaultWelteT100Options: WelteT100Options;
|