linked-rolls 0.46.0 → 0.48.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 +35 -2
- package/lib/Collation.d.ts +21 -0
- package/lib/Collation.js +24 -1
- package/lib/RollCopy.d.ts +12 -1
- package/lib/alignment.d.ts +39 -0
- package/lib/alignment.js +60 -1
- package/lib/editionOps.d.ts +24 -8
- package/lib/editionOps.js +58 -17
- package/lib/readers/phillipsEroll.d.ts +29 -11
- package/lib/readers/phillipsEroll.js +21 -12
- package/lib/schema.json +6 -1
- package/lib/spec/context.json +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -143,17 +143,50 @@ and the onset alone 39, so neither test is idle, but a difference at the
|
|
|
143
143
|
end is as often a punch measured badly as a punch genuinely prolonged,
|
|
144
144
|
and the two are not worth confusing.
|
|
145
145
|
|
|
146
|
+
## What a reader adds to a hole
|
|
147
|
+
|
|
148
|
+
A copy read pneumatically rather than scanned reports how long a valve
|
|
149
|
+
stayed open, and a valve is held on past the perforation that opened it,
|
|
150
|
+
so its holes run longer than the punched slots while their onsets agree.
|
|
151
|
+
Left in, that is not a difference between copies: a collation comparing
|
|
152
|
+
such a copy's hole ends against a scanned copy's compares an on-time
|
|
153
|
+
with a slot, and reads the one as a lengthening of the other.
|
|
154
|
+
|
|
155
|
+
`shortenHoles` takes the extension off and records it on the copy,
|
|
156
|
+
`revertShortening` puts it back, and `readFromPhillipsEroll` will take
|
|
157
|
+
it off at import. Only holes are touched, what a writing spans being no
|
|
158
|
+
valve. The figure is measured against the other copies rather than
|
|
159
|
+
derived from the bar, since Phillips gives only the difference between
|
|
160
|
+
two bars and not the absolute.
|
|
161
|
+
|
|
162
|
+
On welte225.org that figure is about 1.6 mm, and its shape is simple. It
|
|
163
|
+
does not grow with the length of the perforation: the ratio of the read
|
|
164
|
+
length to the true one falls from 1.34 on the shortest punches to 1.01
|
|
165
|
+
on the longest, which is what a constant added to everything does. There
|
|
166
|
+
is no shortest on-time the valve cannot fall below. It shows no run
|
|
167
|
+
along the roll, though one roll's span of paper speed is too narrow to
|
|
168
|
+
tell a constant in millimetres from a constant in time. What it does
|
|
169
|
+
vary with is the port that read it, by about half a millimetre either
|
|
170
|
+
way and with no gradient across the bar, which is measured but too
|
|
171
|
+
coarse to model from a single roll.
|
|
172
|
+
|
|
146
173
|
## Collating a derivation a second time
|
|
147
174
|
|
|
148
175
|
A collated symbol is one symbol carrying every copy that reads it, so a
|
|
149
176
|
collation cannot simply be run again: there is no second symbol left to
|
|
150
|
-
match. `separateReadings` is the inverse. It takes
|
|
151
|
-
back off the symbols it shares, gives them to new symbols of the
|
|
177
|
+
match. `separateReadings` is the inverse. It takes the carriers of one
|
|
178
|
+
side back off the symbols it shares, gives them to new symbols of the
|
|
152
179
|
version's own, and states the exchange, leaving the edition in the state
|
|
153
180
|
a collation would have produced had nothing joined. `connectVersions` at
|
|
154
181
|
a new tolerance then re-collates, joining again whatever the new window
|
|
155
182
|
admits.
|
|
156
183
|
|
|
184
|
+
It takes a side and not a copy, the same side `sidesOf` names and a
|
|
185
|
+
window is measured over. Naming one copy of a side that has several does
|
|
186
|
+
not separate that side: the rest of it stays behind on the other side's
|
|
187
|
+
symbol, and a collation then compares one copy against that mixture
|
|
188
|
+
rather than the two texts against each other.
|
|
189
|
+
|
|
157
190
|
Nothing an editor established is lost by that. `connectVersions`
|
|
158
191
|
rewrites only what a collation wrote (`isCollationsOwn`): a bare
|
|
159
192
|
insertion or deletion, or an equivalence between two systems' spellings.
|
package/lib/Collation.d.ts
CHANGED
|
@@ -85,6 +85,27 @@ export type Collation = {
|
|
|
85
85
|
};
|
|
86
86
|
/** Each of the own symbols with every inherited symbol it collates with, both in the order given. */
|
|
87
87
|
export declare const collationsOf: (own: readonly Readonly<AnySymbol>[], inherited: readonly Readonly<AnySymbol>[], locate: Locate, tolerance?: CollationTolerance) => Collation[];
|
|
88
|
+
/**
|
|
89
|
+
* The motivation a collation writes on what it makes, saying that
|
|
90
|
+
* nobody has read it yet.
|
|
91
|
+
*
|
|
92
|
+
* A collation produces its edits by a rule, and an edition that
|
|
93
|
+
* publishes them unmarked would present a machine's output as an
|
|
94
|
+
* editor's reading. Stating that they are unchecked is the honest
|
|
95
|
+
* middle: the readings are there to be seen and argued with, and a
|
|
96
|
+
* reader can tell which of them anybody has weighed.
|
|
97
|
+
*
|
|
98
|
+
* It does not make an edit the editor's. `isCollationsOwn` passes over
|
|
99
|
+
* this motivation exactly, so a collation rewrites what it marked
|
|
100
|
+
* before, and only what somebody has actually written stays.
|
|
101
|
+
*/
|
|
102
|
+
export declare const unchecked = "unchecked";
|
|
103
|
+
/** The version's statement of what the unchecked motivation means, to stand in its `motivations`. */
|
|
104
|
+
export declare const uncheckedMotivation: {
|
|
105
|
+
readonly type: 'motivation';
|
|
106
|
+
readonly id: "unchecked";
|
|
107
|
+
readonly note: 'Made by collating the two texts and not yet read by an editor.';
|
|
108
|
+
};
|
|
88
109
|
/**
|
|
89
110
|
* Whether the edit is one a collation writes by itself: a bare
|
|
90
111
|
* insertion or a bare deletion saying nothing further, or an
|
package/lib/Collation.js
CHANGED
|
@@ -89,6 +89,27 @@ export const collationsOf = (own, inherited, locate, tolerance = defaultCollatio
|
|
|
89
89
|
};
|
|
90
90
|
/** The edit type a collation draws by itself, from the two systems' vocabularies rather than off the paper. */
|
|
91
91
|
const drawnByCollation = 'replace-with-equivalent';
|
|
92
|
+
/**
|
|
93
|
+
* The motivation a collation writes on what it makes, saying that
|
|
94
|
+
* nobody has read it yet.
|
|
95
|
+
*
|
|
96
|
+
* A collation produces its edits by a rule, and an edition that
|
|
97
|
+
* publishes them unmarked would present a machine's output as an
|
|
98
|
+
* editor's reading. Stating that they are unchecked is the honest
|
|
99
|
+
* middle: the readings are there to be seen and argued with, and a
|
|
100
|
+
* reader can tell which of them anybody has weighed.
|
|
101
|
+
*
|
|
102
|
+
* It does not make an edit the editor's. `isCollationsOwn` passes over
|
|
103
|
+
* this motivation exactly, so a collation rewrites what it marked
|
|
104
|
+
* before, and only what somebody has actually written stays.
|
|
105
|
+
*/
|
|
106
|
+
export const unchecked = 'unchecked';
|
|
107
|
+
/** The version's statement of what the unchecked motivation means, to stand in its `motivations`. */
|
|
108
|
+
export const uncheckedMotivation = {
|
|
109
|
+
type: 'motivation',
|
|
110
|
+
id: unchecked,
|
|
111
|
+
note: 'Made by collating the two texts and not yet read by an editor.'
|
|
112
|
+
};
|
|
92
113
|
/**
|
|
93
114
|
* Whether the edit is one a collation writes by itself: a bare
|
|
94
115
|
* insertion or a bare deletion saying nothing further, or an
|
|
@@ -107,4 +128,6 @@ const drawnByCollation = 'replace-with-equivalent';
|
|
|
107
128
|
* draws a second time over the very same symbols.
|
|
108
129
|
*/
|
|
109
130
|
export const isCollationsOwn = (edit) => edit.editType === drawnByCollation
|
|
110
|
-
|| (edit.editType === undefined
|
|
131
|
+
|| (edit.editType === undefined
|
|
132
|
+
&& (edit.motivation === undefined || edit.motivation === unchecked)
|
|
133
|
+
&& edit['@annotation'] === undefined);
|
package/lib/RollCopy.d.ts
CHANGED
|
@@ -201,7 +201,7 @@ export interface RollCopy extends WithType<'RollCopy'>, WithId {
|
|
|
201
201
|
* (e.g. 'shifted', 'stretched') to normalize measurements
|
|
202
202
|
* for comparison with other copies. Not exported to RDF.
|
|
203
203
|
*/
|
|
204
|
-
ops: Array<'shifted' | 'stretched'>;
|
|
204
|
+
ops: Array<'shifted' | 'stretched' | 'shortened'>;
|
|
205
205
|
/**
|
|
206
206
|
* Physical measurements of this roll copy, including
|
|
207
207
|
* dimensions, punch diameter, hole separation, margins,
|
|
@@ -258,6 +258,17 @@ export interface RollCopy extends WithType<'RollCopy'>, WithId {
|
|
|
258
258
|
* Not exported to RDF.
|
|
259
259
|
*/
|
|
260
260
|
scale: number;
|
|
261
|
+
/**
|
|
262
|
+
* How much longer this copy's reading of a hole ran than the
|
|
263
|
+
* perforation that caused it, where that was taken off the ends
|
|
264
|
+
* again. A pneumatic reader reports how long a valve stayed
|
|
265
|
+
* open, which exceeds the perforation that opened it, so its
|
|
266
|
+
* holes are overlong by a constant while its onsets agree.
|
|
267
|
+
* Nothing for a copy read by other means, whose holes are the
|
|
268
|
+
* punched slots themselves.
|
|
269
|
+
* Not exported to RDF.
|
|
270
|
+
*/
|
|
271
|
+
readerExtension: Millimeters;
|
|
261
272
|
/**
|
|
262
273
|
* The resolution this copy's scan was read at, along the roll.
|
|
263
274
|
* It is what the measurements given in pixels are to be read
|
package/lib/alignment.d.ts
CHANGED
|
@@ -9,6 +9,45 @@ export declare const applyScale: (factor: number, copy: RollCopy) => void;
|
|
|
9
9
|
export declare const revertShift: (copy: RollCopy) => void;
|
|
10
10
|
/** Takes the scale off the copy's features again, as far as one was applied. */
|
|
11
11
|
export declare const revertScale: (copy: RollCopy) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Takes the extension a pneumatic reader adds off the ends of the
|
|
14
|
+
* copy's holes, and records how much was taken.
|
|
15
|
+
*
|
|
16
|
+
* Such a reader reports how long a valve stayed open, and a valve is
|
|
17
|
+
* held on past the perforation that opened it, so its holes run longer
|
|
18
|
+
* than the punched slots while their onsets agree. Left in, the
|
|
19
|
+
* difference is not a difference between copies at all: a collation
|
|
20
|
+
* comparing this copy's hole ends against a scanned copy's compares a
|
|
21
|
+
* pneumatic on-time with a punched slot, and reads the one as a
|
|
22
|
+
* lengthening of the other.
|
|
23
|
+
*
|
|
24
|
+
* The extension is a property of the reader rather than of the roll,
|
|
25
|
+
* and on the material measured so far it is a constant: it does not
|
|
26
|
+
* grow with the length of the perforation, there is no shortest
|
|
27
|
+
* on-time the valve cannot fall below, and it does not run along the
|
|
28
|
+
* roll with the paper speed as far as one roll can show. What it does
|
|
29
|
+
* vary with is the port that read it, by about half a millimetre either
|
|
30
|
+
* way and without any gradient across the bar, which is measured but
|
|
31
|
+
* too coarse to model from one roll.
|
|
32
|
+
*
|
|
33
|
+
* Only holes are touched. What a writing or a mark spans is no valve.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* The holes the extension cannot be taken off, being no longer than it
|
|
37
|
+
* is: the reader reported them open for less time than it holds a valve
|
|
38
|
+
* on beyond the perforation, so the constant over-corrects them and
|
|
39
|
+
* would leave them ending before they begin.
|
|
40
|
+
*
|
|
41
|
+
* They are where the constant shows its limit rather than where the
|
|
42
|
+
* copy is wrong, the extension varying by about half a millimetre with
|
|
43
|
+
* the port. What to do with them is an editorial question — a condition
|
|
44
|
+
* on the feature, a reading of the hole, a smaller extension — and
|
|
45
|
+
* `shortenHoles` throws rather than answer it.
|
|
46
|
+
*/
|
|
47
|
+
export declare const tooShortToShorten: (extension: Millimeters, copy: RollCopy) => Readonly<FeatureOrPatch>[];
|
|
48
|
+
export declare const shortenHoles: (extension: Millimeters, copy: RollCopy) => void;
|
|
49
|
+
/** Puts the reader's extension back on the copy's holes, as far as one was taken off. */
|
|
50
|
+
export declare const revertShortening: (copy: RollCopy) => void;
|
|
12
51
|
/**
|
|
13
52
|
* How a copy's places are carried onto another copy's:
|
|
14
53
|
* `x_other = (x + shift) · scale`.
|
package/lib/alignment.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { featuresOf } from "./RollCopy.js";
|
|
2
2
|
import { welteT100 } from "./systems/welteT100/bar.js";
|
|
3
|
-
import { add, mm, scale } from "./Quantity.js";
|
|
3
|
+
import { add, mm, scale, subtract } from "./Quantity.js";
|
|
4
4
|
/** Moves both ends of a span, the far end only where the span has one. */
|
|
5
5
|
const move = (span, by) => {
|
|
6
6
|
span.from = add(span.from, by);
|
|
@@ -54,6 +54,65 @@ export const revertScale = (copy) => {
|
|
|
54
54
|
copy.ops = copy.ops.filter(op => op !== 'stretched');
|
|
55
55
|
delete copy.measurements.scale;
|
|
56
56
|
};
|
|
57
|
+
const holesOf = (copy) => featuresOf(copy).filter(feature => feature.type === 'Hole');
|
|
58
|
+
/**
|
|
59
|
+
* Takes the extension a pneumatic reader adds off the ends of the
|
|
60
|
+
* copy's holes, and records how much was taken.
|
|
61
|
+
*
|
|
62
|
+
* Such a reader reports how long a valve stayed open, and a valve is
|
|
63
|
+
* held on past the perforation that opened it, so its holes run longer
|
|
64
|
+
* than the punched slots while their onsets agree. Left in, the
|
|
65
|
+
* difference is not a difference between copies at all: a collation
|
|
66
|
+
* comparing this copy's hole ends against a scanned copy's compares a
|
|
67
|
+
* pneumatic on-time with a punched slot, and reads the one as a
|
|
68
|
+
* lengthening of the other.
|
|
69
|
+
*
|
|
70
|
+
* The extension is a property of the reader rather than of the roll,
|
|
71
|
+
* and on the material measured so far it is a constant: it does not
|
|
72
|
+
* grow with the length of the perforation, there is no shortest
|
|
73
|
+
* on-time the valve cannot fall below, and it does not run along the
|
|
74
|
+
* roll with the paper speed as far as one roll can show. What it does
|
|
75
|
+
* vary with is the port that read it, by about half a millimetre either
|
|
76
|
+
* way and without any gradient across the bar, which is measured but
|
|
77
|
+
* too coarse to model from one roll.
|
|
78
|
+
*
|
|
79
|
+
* Only holes are touched. What a writing or a mark spans is no valve.
|
|
80
|
+
*/
|
|
81
|
+
/**
|
|
82
|
+
* The holes the extension cannot be taken off, being no longer than it
|
|
83
|
+
* is: the reader reported them open for less time than it holds a valve
|
|
84
|
+
* on beyond the perforation, so the constant over-corrects them and
|
|
85
|
+
* would leave them ending before they begin.
|
|
86
|
+
*
|
|
87
|
+
* They are where the constant shows its limit rather than where the
|
|
88
|
+
* copy is wrong, the extension varying by about half a millimetre with
|
|
89
|
+
* the port. What to do with them is an editorial question — a condition
|
|
90
|
+
* on the feature, a reading of the hole, a smaller extension — and
|
|
91
|
+
* `shortenHoles` throws rather than answer it.
|
|
92
|
+
*/
|
|
93
|
+
export const tooShortToShorten = (extension, copy) => holesOf(copy).filter(hole => hole.horizontal.to - hole.horizontal.from <= extension);
|
|
94
|
+
export const shortenHoles = (extension, copy) => {
|
|
95
|
+
if (copy.ops.includes('shortened'))
|
|
96
|
+
return;
|
|
97
|
+
const tooShort = tooShortToShorten(extension, copy);
|
|
98
|
+
if (tooShort.length > 0) {
|
|
99
|
+
throw new Error(`The extension of ${extension} mm cannot be taken off ${tooShort.length} `
|
|
100
|
+
+ `hole(s) of copy ${copy.id}, which are no longer than it: `
|
|
101
|
+
+ `${tooShort.map(hole => hole.id).join(', ')}`);
|
|
102
|
+
}
|
|
103
|
+
holesOf(copy).forEach(hole => { hole.horizontal.to = subtract(hole.horizontal.to, extension); });
|
|
104
|
+
copy.ops = [...copy.ops, 'shortened'];
|
|
105
|
+
copy.measurements.readerExtension = extension;
|
|
106
|
+
};
|
|
107
|
+
/** Puts the reader's extension back on the copy's holes, as far as one was taken off. */
|
|
108
|
+
export const revertShortening = (copy) => {
|
|
109
|
+
const extension = copy.measurements.readerExtension;
|
|
110
|
+
if (!copy.ops.includes('shortened') || extension === undefined)
|
|
111
|
+
return;
|
|
112
|
+
holesOf(copy).forEach(hole => { hole.horizontal.to = add(hole.horizontal.to, extension); });
|
|
113
|
+
copy.ops = copy.ops.filter(op => op !== 'shortened');
|
|
114
|
+
delete copy.measurements.readerExtension;
|
|
115
|
+
};
|
|
57
116
|
const byPlace = (x, y) => x.at - y.at || x.pitch - y.pitch;
|
|
58
117
|
/** The notes the bar reads off the features, in the order they pass it. */
|
|
59
118
|
const noteOnsets = (features, bar) => features
|
package/lib/editionOps.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { GeneralRollCondition, ModificationPurpose, RollCopy, ScaleReading, Shif
|
|
|
8
8
|
import { FeatureSource } from "./FeatureSource.js";
|
|
9
9
|
import { AnyArgumentation, Belief, Certainty, ObjectAssumption } from "./Assumption.js";
|
|
10
10
|
import { FeatureConditionAssignment, FeatureOrPatch, NestedFeature } from "./Feature.js";
|
|
11
|
+
import { Millimeters } from "./Quantity.js";
|
|
11
12
|
/**
|
|
12
13
|
* A change to an edition, written onto an immer draft of it. One
|
|
13
14
|
* operation is one undo step, so an operation that has to read the
|
|
@@ -40,6 +41,14 @@ export declare const alignCopy: (copyId: string, shift: Shift, scale: number, re
|
|
|
40
41
|
* being a fact about the copy.
|
|
41
42
|
*/
|
|
42
43
|
export declare const unalignCopy: (copyId: string) => EditionOp;
|
|
44
|
+
/**
|
|
45
|
+
* Takes the extension a pneumatic reader adds off the ends of the
|
|
46
|
+
* copy's holes, and records how much was taken, so that its lengths
|
|
47
|
+
* can be compared with a scanned copy's at all.
|
|
48
|
+
*/
|
|
49
|
+
export declare const shortenCopy: (copyId: string, extension: Millimeters) => EditionOp;
|
|
50
|
+
/** Puts the reader's extension back on the copy's holes. */
|
|
51
|
+
export declare const unshortenCopy: (copyId: string) => EditionOp;
|
|
43
52
|
/** States what the copy's features were read from, in place of any earlier statement. */
|
|
44
53
|
export declare const stateSource: (copyId: string, source: FeatureSource) => EditionOp;
|
|
45
54
|
/** Takes back the statement, leaving the copy silent about its source again. */
|
|
@@ -180,9 +189,9 @@ export declare const connectVersions: (view: EditionView, childId: string, paren
|
|
|
180
189
|
*/
|
|
181
190
|
export declare const collateSymbols: (view: EditionView, versionId: string, symbolIds: readonly string[], tolerance?: CollationTolerance) => EditionOp;
|
|
182
191
|
/**
|
|
183
|
-
* Takes the
|
|
184
|
-
* collated into:
|
|
185
|
-
* version's own, the
|
|
192
|
+
* Takes the named copies' reading of a symbol back out of the symbol it
|
|
193
|
+
* was collated into: their carriers pass to a new symbol of the
|
|
194
|
+
* version's own, the remaining copies keep the symbol they had, and the
|
|
186
195
|
* version states the exchange.
|
|
187
196
|
*
|
|
188
197
|
* This is the inverse of the hand-over a collation makes, and it is
|
|
@@ -191,12 +200,19 @@ export declare const collateSymbols: (view: EditionView, versionId: string, symb
|
|
|
191
200
|
* takes the two readings apart again, and without that a tolerance
|
|
192
201
|
* arrived at after the fact could never be applied.
|
|
193
202
|
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
203
|
+
* The copies named are one **side** of the derivation, the same side a
|
|
204
|
+
* window is measured over, and `sidesOf` is what says which they are.
|
|
205
|
+
* Naming one copy of a side that has several does not separate that
|
|
206
|
+
* side: what stays behind is the rest of it, mixed with the other
|
|
207
|
+
* side's copies, and a collation then compares one copy against that
|
|
208
|
+
* mixture rather than the two texts against each other.
|
|
209
|
+
*
|
|
210
|
+
* Symbols the named copies alone carry are already the version's own
|
|
211
|
+
* and are passed over, so a reading somebody has separated by hand
|
|
212
|
+
* keeps its identifier and the edit that speaks for it. Named symbols
|
|
213
|
+
* narrow the act to those; naming none separates the whole reading.
|
|
198
214
|
*/
|
|
199
|
-
export declare const separateReadings: (view: EditionView, versionId: string,
|
|
215
|
+
export declare const separateReadings: (view: EditionView, versionId: string, copies: ReadonlySet<string>, symbolIds?: readonly string[]) => EditionOp;
|
|
200
216
|
/**
|
|
201
217
|
* Makes the version stand on its own: what it inherited becomes its
|
|
202
218
|
* own insertions, and its derivations go, the hypotheses among them,
|
package/lib/editionOps.js
CHANGED
|
@@ -2,13 +2,13 @@ import { current, isDraft } from "immer";
|
|
|
2
2
|
import { v4 } from "uuid";
|
|
3
3
|
import { getAt } from "./EditionView.js";
|
|
4
4
|
import { isCommand, placementRelations } from "./Symbol.js";
|
|
5
|
-
import { collationsOf, defaultCollationTolerance, isCollationsOwn } from "./Collation.js";
|
|
5
|
+
import { collationsOf, defaultCollationTolerance, isCollationsOwn, unchecked, uncheckedMotivation } from "./Collation.js";
|
|
6
6
|
import { collationToleranceOf, editsOf, insertedBy, principalDerivationOf } from "./Version.js";
|
|
7
7
|
import { asSymbols, barOf, featuresByAct, featuresOf, isPaperStretch, statesNothing } from "./RollCopy.js";
|
|
8
8
|
import { systemOf } from "./TrackerBar.js";
|
|
9
9
|
import { substitutionsBetween } from "./substitution.js";
|
|
10
10
|
import { trackerBarOf } from "./systems/index.js";
|
|
11
|
-
import { applyShift, applyScale, revertShift, revertScale } from "./alignment.js";
|
|
11
|
+
import { applyShift, applyScale, revertShift, revertScale, revertShortening, shortenHoles } from "./alignment.js";
|
|
12
12
|
import { assignReference, idOf } from "./Assumption.js";
|
|
13
13
|
import { conditions as conditionsAllowed, featuresBorneBy, isGluedOn, withBorneFeatures } from "./Feature.js";
|
|
14
14
|
import { distance, mm, subtract } from "./Quantity.js";
|
|
@@ -54,6 +54,17 @@ const droppedIds = (before, after) => {
|
|
|
54
54
|
};
|
|
55
55
|
const insertion = (symbol) => ({ type: 'edit', id: v4(), insert: [symbol] });
|
|
56
56
|
const deletion = (symbolId) => ({ type: 'edit', id: v4(), delete: [symbolId] });
|
|
57
|
+
/** The edit as a collation leaves it: made by rule, and not yet read by anybody. */
|
|
58
|
+
const asUnchecked = (edit) => ({ ...edit, motivation: unchecked });
|
|
59
|
+
/**
|
|
60
|
+
* The motivations with the unchecked one declared, where any edit
|
|
61
|
+
* names it and the version does not state it yet. An edit naming a
|
|
62
|
+
* motivation the version leaves undeclared points at nothing.
|
|
63
|
+
*/
|
|
64
|
+
const declaring = (motivations, edits) => edits.some(edit => edit.motivation === unchecked)
|
|
65
|
+
&& !motivations.some(motivation => motivation.id === unchecked)
|
|
66
|
+
? [...motivations, uncheckedMotivation]
|
|
67
|
+
: motivations;
|
|
57
68
|
const isEmpty = (edit) => !edit.insert?.length && !edit.delete?.length;
|
|
58
69
|
const insertedIn = (versions) => versions.flatMap(insertedBy);
|
|
59
70
|
/** The edits with the change applied, less those it emptied; the very same array where it changed none. */
|
|
@@ -125,6 +136,14 @@ export const unalignCopy = (copyId) => onCopy(copyId, copy => {
|
|
|
125
136
|
revertShift(copy);
|
|
126
137
|
copy.conditions = without(copy.conditions, isPaperStretch);
|
|
127
138
|
});
|
|
139
|
+
/**
|
|
140
|
+
* Takes the extension a pneumatic reader adds off the ends of the
|
|
141
|
+
* copy's holes, and records how much was taken, so that its lengths
|
|
142
|
+
* can be compared with a scanned copy's at all.
|
|
143
|
+
*/
|
|
144
|
+
export const shortenCopy = (copyId, extension) => onCopy(copyId, copy => shortenHoles(extension, copy));
|
|
145
|
+
/** Puts the reader's extension back on the copy's holes. */
|
|
146
|
+
export const unshortenCopy = (copyId) => onCopy(copyId, copy => revertShortening(copy));
|
|
128
147
|
/** States what the copy's features were read from, in place of any earlier statement. */
|
|
129
148
|
export const stateSource = (copyId, source) => onCopy(copyId, copy => {
|
|
130
149
|
copy.readFrom = source;
|
|
@@ -725,6 +744,7 @@ export const connectVersions = (view, childId, parentId, tolerance = defaultColl
|
|
|
725
744
|
type: 'edit',
|
|
726
745
|
id: v4(),
|
|
727
746
|
editType: 'replace-with-equivalent',
|
|
747
|
+
motivation: unchecked,
|
|
728
748
|
insert: [...by],
|
|
729
749
|
delete: deleted
|
|
730
750
|
};
|
|
@@ -732,14 +752,15 @@ export const connectVersions = (view, childId, parentId, tolerance = defaultColl
|
|
|
732
752
|
const edits = [
|
|
733
753
|
...established,
|
|
734
754
|
...substituted.map(equivalence),
|
|
735
|
-
...own.filter(symbol => !collated.has(symbol.id) && !paired.has(symbol.id)).map(insertion),
|
|
755
|
+
...own.filter(symbol => !collated.has(symbol.id) && !paired.has(symbol.id)).map(insertion).map(asUnchecked),
|
|
736
756
|
...inherited
|
|
737
757
|
.filter(symbol => !matched.has(symbol.id) && !paired.has(symbol.id))
|
|
738
|
-
.map(symbol => deletion(symbol.id))
|
|
758
|
+
.map(symbol => asUnchecked(deletion(symbol.id)))
|
|
739
759
|
];
|
|
740
760
|
return onVersion(childId, (child, draft) => {
|
|
741
761
|
handOverCarriers(view, draft, collations);
|
|
742
762
|
child.edits = edits;
|
|
763
|
+
child.motivations = declaring(stateOf(child).motivations, edits);
|
|
743
764
|
child.basedOn = [
|
|
744
765
|
{ ...assignReference(parentId), collationTolerance: tolerance },
|
|
745
766
|
...hypothesesBeside(stateOf(child), parentId)
|
|
@@ -778,16 +799,29 @@ const readingOf = (symbol, carriers) => {
|
|
|
778
799
|
}
|
|
779
800
|
};
|
|
780
801
|
/** The symbol where the copy and at least one other carry it, with the carriers of each side. */
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
802
|
+
/**
|
|
803
|
+
* The symbol where the named copies and at least one other carry it,
|
|
804
|
+
* with the carriers of each side.
|
|
805
|
+
*
|
|
806
|
+
* A symbol the named copies alone carry has no other side to be
|
|
807
|
+
* separated from and is passed over. That covers the insertions, and it
|
|
808
|
+
* covers a symbol an ancestor holds on one branch's witness alone,
|
|
809
|
+
* where the two versions do not disagree and there is nothing to take
|
|
810
|
+
* apart.
|
|
811
|
+
*/
|
|
812
|
+
const sharedWith = (view, symbol, copies) => {
|
|
813
|
+
const onNamedCopy = (carrier) => {
|
|
814
|
+
const copy = view.copyOf(idOf(carrier));
|
|
815
|
+
return copy !== undefined && copies.has(copy.id);
|
|
816
|
+
};
|
|
817
|
+
const mine = symbol.carriers.filter(onNamedCopy);
|
|
818
|
+
const theirs = symbol.carriers.filter(carrier => !onNamedCopy(carrier));
|
|
785
819
|
return mine.length > 0 && theirs.length > 0 ? [{ symbol, mine, theirs }] : [];
|
|
786
820
|
};
|
|
787
821
|
/**
|
|
788
|
-
* Takes the
|
|
789
|
-
* collated into:
|
|
790
|
-
* version's own, the
|
|
822
|
+
* Takes the named copies' reading of a symbol back out of the symbol it
|
|
823
|
+
* was collated into: their carriers pass to a new symbol of the
|
|
824
|
+
* version's own, the remaining copies keep the symbol they had, and the
|
|
791
825
|
* version states the exchange.
|
|
792
826
|
*
|
|
793
827
|
* This is the inverse of the hand-over a collation makes, and it is
|
|
@@ -796,19 +830,26 @@ const sharedWith = (view, symbol, copyId) => {
|
|
|
796
830
|
* takes the two readings apart again, and without that a tolerance
|
|
797
831
|
* arrived at after the fact could never be applied.
|
|
798
832
|
*
|
|
799
|
-
*
|
|
800
|
-
*
|
|
801
|
-
*
|
|
802
|
-
*
|
|
833
|
+
* The copies named are one **side** of the derivation, the same side a
|
|
834
|
+
* window is measured over, and `sidesOf` is what says which they are.
|
|
835
|
+
* Naming one copy of a side that has several does not separate that
|
|
836
|
+
* side: what stays behind is the rest of it, mixed with the other
|
|
837
|
+
* side's copies, and a collation then compares one copy against that
|
|
838
|
+
* mixture rather than the two texts against each other.
|
|
839
|
+
*
|
|
840
|
+
* Symbols the named copies alone carry are already the version's own
|
|
841
|
+
* and are passed over, so a reading somebody has separated by hand
|
|
842
|
+
* keeps its identifier and the edit that speaks for it. Named symbols
|
|
843
|
+
* narrow the act to those; naming none separates the whole reading.
|
|
803
844
|
*/
|
|
804
|
-
export const separateReadings = (view, versionId,
|
|
845
|
+
export const separateReadings = (view, versionId, copies, symbolIds) => {
|
|
805
846
|
const version = view.get(versionId);
|
|
806
847
|
if (!version)
|
|
807
848
|
return noChange;
|
|
808
849
|
const chosen = symbolIds && new Set(symbolIds);
|
|
809
850
|
const shared = view.snapshot(versionId)
|
|
810
851
|
.filter(symbol => chosen === undefined || chosen.has(symbol.id))
|
|
811
|
-
.flatMap(symbol => sharedWith(view, symbol,
|
|
852
|
+
.flatMap(symbol => sharedWith(view, symbol, copies));
|
|
812
853
|
if (shared.length === 0)
|
|
813
854
|
return noChange;
|
|
814
855
|
const inserted = new Set(insertedBy(version).map(symbol => symbol.id));
|
|
@@ -20,16 +20,21 @@ import { Millimeters, Seconds } from "../Quantity.js";
|
|
|
20
20
|
* perforation, the height of the tracker bar hole and the paper speed
|
|
21
21
|
* (p. 180).
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
23
|
+
* Left in, that extension is not a difference between copies at all:
|
|
24
|
+
* the end of a hole here is a pneumatic on-time where the end of a
|
|
25
|
+
* scanned copy's hole is a punched slot, so a collation comparing the
|
|
26
|
+
* two reads the one as a lengthening of the other. `extension` takes it
|
|
27
|
+
* off at the end of every hole, and the copy records what was taken.
|
|
28
|
+
*
|
|
29
|
+
* How much to take off is measured rather than derived, since he gives
|
|
30
|
+
* only the 0.5 mm by which the Welte bar's holes exceed the Licensee's
|
|
31
|
+
* and not the absolute. Across welte225.org it comes to about 1.6 mm,
|
|
32
|
+
* and the shape of it is simple: a constant, not growing with the
|
|
33
|
+
* length of the perforation, with no shortest on-time the valve cannot
|
|
34
|
+
* fall below, and with no run along the roll that one roll's span of
|
|
35
|
+
* paper speed could show. The port that read it matters by about half a
|
|
36
|
+
* millimetre either way, without any gradient across the bar, which is
|
|
37
|
+
* measured but too coarse to model from one roll.
|
|
33
38
|
*/
|
|
34
39
|
/**
|
|
35
40
|
* He numbers his files by two rules at once, which his free samples
|
|
@@ -64,12 +69,25 @@ export interface PhillipsErollOptions {
|
|
|
64
69
|
* account for it.
|
|
65
70
|
*/
|
|
66
71
|
placeAt?: (elapsed: Seconds) => Millimeters;
|
|
72
|
+
/**
|
|
73
|
+
* How much longer a valve is held on than the perforation that
|
|
74
|
+
* opened it, taken off the end of every hole so that these lengths
|
|
75
|
+
* can be compared with a scanned copy's. Nothing leaves the holes
|
|
76
|
+
* as the reader reported them, which is what every copy read before
|
|
77
|
+
* this option existed states.
|
|
78
|
+
*
|
|
79
|
+
* The copy records what was taken, and `revertShortening` puts it
|
|
80
|
+
* back, so a figure revised later needs no second import. On
|
|
81
|
+
* welte225.org the extension measures about 1.6 mm, with no
|
|
82
|
+
* dependence on how long the perforation is.
|
|
83
|
+
*/
|
|
84
|
+
extension?: Millimeters;
|
|
67
85
|
}
|
|
68
86
|
/**
|
|
69
87
|
* Reads one of his e-roll files as a copy of the roll, in millimetres
|
|
70
88
|
* of paper, on the bar it was cut for and in that bar's numbering. A
|
|
71
89
|
* position the bar does not read is left out, as the bar would leave it.
|
|
72
90
|
*/
|
|
73
|
-
export declare function readFromPhillipsEroll(buffer: ArrayBuffer, { system, controlOffset, placeAt }?: PhillipsErollOptions): RollCopy;
|
|
91
|
+
export declare function readFromPhillipsEroll(buffer: ArrayBuffer, { system, controlOffset, placeAt, extension }?: PhillipsErollOptions): RollCopy;
|
|
74
92
|
/** The bars his files are known to be numbered by. */
|
|
75
93
|
export declare const phillipsSystems: readonly TrackerBar[];
|
|
@@ -2,6 +2,7 @@ import { read } from "midifile-ts";
|
|
|
2
2
|
import { v4 } from "uuid";
|
|
3
3
|
import { assignObject } from "../Assumption.js";
|
|
4
4
|
import { systemOf } from "../TrackerBar.js";
|
|
5
|
+
import { shortenHoles } from "../alignment.js";
|
|
5
6
|
import { welteT100 } from "../systems/welteT100/bar.js";
|
|
6
7
|
import { welteLicensee } from "../systems/welteLicensee/bar.js";
|
|
7
8
|
import { inMetersPerMinute, mm, seconds, track } from "../Quantity.js";
|
|
@@ -24,16 +25,21 @@ import { inMetersPerMinute, mm, seconds, track } from "../Quantity.js";
|
|
|
24
25
|
* perforation, the height of the tracker bar hole and the paper speed
|
|
25
26
|
* (p. 180).
|
|
26
27
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
28
|
+
* Left in, that extension is not a difference between copies at all:
|
|
29
|
+
* the end of a hole here is a pneumatic on-time where the end of a
|
|
30
|
+
* scanned copy's hole is a punched slot, so a collation comparing the
|
|
31
|
+
* two reads the one as a lengthening of the other. `extension` takes it
|
|
32
|
+
* off at the end of every hole, and the copy records what was taken.
|
|
33
|
+
*
|
|
34
|
+
* How much to take off is measured rather than derived, since he gives
|
|
35
|
+
* only the 0.5 mm by which the Welte bar's holes exceed the Licensee's
|
|
36
|
+
* and not the absolute. Across welte225.org it comes to about 1.6 mm,
|
|
37
|
+
* and the shape of it is simple: a constant, not growing with the
|
|
38
|
+
* length of the perforation, with no shortest on-time the valve cannot
|
|
39
|
+
* fall below, and with no run along the roll that one roll's span of
|
|
40
|
+
* paper speed could show. The port that read it matters by about half a
|
|
41
|
+
* millimetre either way, without any gradient across the bar, which is
|
|
42
|
+
* measured but too coarse to model from one roll.
|
|
37
43
|
*/
|
|
38
44
|
/**
|
|
39
45
|
* He numbers his files by two rules at once, which his free samples
|
|
@@ -134,7 +140,7 @@ const positionsByPitch = (bar) => new Map(Array.from({ length: bar.trackCount },
|
|
|
134
140
|
* of paper, on the bar it was cut for and in that bar's numbering. A
|
|
135
141
|
* position the bar does not read is left out, as the bar would leave it.
|
|
136
142
|
*/
|
|
137
|
-
export function readFromPhillipsEroll(buffer, { system = welteT100, controlOffset, placeAt } = {}) {
|
|
143
|
+
export function readFromPhillipsEroll(buffer, { system = welteT100, controlOffset, placeAt, extension } = {}) {
|
|
138
144
|
const file = read(new Uint8Array(buffer));
|
|
139
145
|
const timed = onOneClock(file.tracks);
|
|
140
146
|
const elapsedAt = clockOf(temposIn(timed), file.header.ticksPerBeat);
|
|
@@ -157,7 +163,7 @@ export function readFromPhillipsEroll(buffer, { system = welteT100, controlOffse
|
|
|
157
163
|
}
|
|
158
164
|
}];
|
|
159
165
|
});
|
|
160
|
-
|
|
166
|
+
const copy = {
|
|
161
167
|
type: 'RollCopy',
|
|
162
168
|
id: v4(),
|
|
163
169
|
ops: [],
|
|
@@ -171,6 +177,9 @@ export function readFromPhillipsEroll(buffer, { system = welteT100, controlOffse
|
|
|
171
177
|
note: 'Read on Phillips’s pneumatic roll reader. The places are elapsed time put back onto the paper, and a hole runs as long as its switch stayed open, which is longer than the perforation.'
|
|
172
178
|
}
|
|
173
179
|
};
|
|
180
|
+
if (extension !== undefined)
|
|
181
|
+
shortenHoles(extension, copy);
|
|
182
|
+
return copy;
|
|
174
183
|
}
|
|
175
184
|
/** The bars his files are known to be numbered by. */
|
|
176
185
|
export const phillipsSystems = [welteT100, welteLicensee];
|
package/lib/schema.json
CHANGED
|
@@ -2828,6 +2828,10 @@
|
|
|
2828
2828
|
"description": "The average diameter of punched holes.",
|
|
2829
2829
|
"ontology": "reo:punchDiameter"
|
|
2830
2830
|
},
|
|
2831
|
+
"readerExtension": {
|
|
2832
|
+
"$ref": "#/definitions/Millimeters",
|
|
2833
|
+
"description": "How much longer this copy's reading of a hole ran than the perforation that caused it, where that was taken off the ends again. A pneumatic reader reports how long a valve stayed open, which exceeds the perforation that opened it, so its holes are overlong by a constant while its onsets agree. Nothing for a copy read by other means, whose holes are the punched slots themselves. Not exported to RDF."
|
|
2834
|
+
},
|
|
2831
2835
|
"scale": {
|
|
2832
2836
|
"description": "The factor this copy's features were scaled by to align them with the others. What it is put down to is stated apart: a paper-stretch condition, or the speed the copy was cut for. Not exported to RDF.",
|
|
2833
2837
|
"type": "number"
|
|
@@ -2861,7 +2865,8 @@
|
|
|
2861
2865
|
"items": {
|
|
2862
2866
|
"enum": [
|
|
2863
2867
|
"shifted",
|
|
2864
|
-
"stretched"
|
|
2868
|
+
"stretched",
|
|
2869
|
+
"shortened"
|
|
2865
2870
|
],
|
|
2866
2871
|
"type": "string"
|
|
2867
2872
|
},
|
package/lib/spec/context.json
CHANGED
package/package.json
CHANGED