linked-rolls 0.65.0 → 0.66.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 CHANGED
@@ -55,6 +55,14 @@ between the two. What the alignments say about the paper of the copies,
55
55
  how far each has stretched and how the papers of the systems relate, is
56
56
  worked out by `paperOf`.
57
57
 
58
+ What is worked out is not stated. Where the edition holds to the ratio a
59
+ re-cut gave the paper of its version, the creation of the version states
60
+ it (`lengthRatio`, as `lengthRatioOf` gives it), so that others can take
61
+ it as a premise. A measured value, such as that ratio, a copy's
62
+ dimensions and hole separation, or the setting of its perforator, is
63
+ held on the strength of a measurement, which names what it was taken
64
+ on, the program and its version, and the day.
65
+
58
66
  Documents written by earlier releases are brought up to date on import.
59
67
  To check a document against the schema:
60
68
 
@@ -1,5 +1,6 @@
1
1
  import { Edition } from "../model/Edition.js";
2
2
  import { Strain } from "../model/RollCopy.js";
3
+ import { LengthRatio } from "../model/Version.js";
3
4
  import { Percent } from "../model/Quantity.js";
4
5
  /**
5
6
  * How far the paper of one copy is taken to have stretched or shrunk
@@ -127,3 +128,19 @@ export type AlignmentProblem = {
127
128
  * is `alignmentFor`'s to say.
128
129
  */
129
130
  export declare const alignmentProblems: (edition: Pick<Edition, 'copies' | 'referenceCopy'>) => AlignmentProblem[];
131
+ /**
132
+ * How long the version's paper runs for a length of the paper of the
133
+ * version it derives from, as the alignments give it: the ratio of the
134
+ * two versions' systems' papers, with its uncertainty. It is what the
135
+ * creation of the version states where the edition holds to it
136
+ * (`VersionCreation.lengthRatio`).
137
+ *
138
+ * The reading pools the copies of a system, so the ratio is only given
139
+ * for a version that one of those copies bears witness to: a second
140
+ * re-cut for the same system, whose copies measure no paper, is not
141
+ * credited with the first one's ratio. Where the reading has copies of
142
+ * two versions re-cut for one system, the ratio is theirs together.
143
+ * Nothing is given for a version that derives from none, or from one on
144
+ * its own system, whose paper it keeps.
145
+ */
146
+ export declare const lengthRatioOf: (edition: Edition, versionId: string) => LengthRatio | undefined;
@@ -1,7 +1,11 @@
1
1
  import { referenceCopyOf } from "../model/Edition.js";
2
2
  import { barOf, featuresOf, paperStretchOf } from "../model/RollCopy.js";
3
- import { certaintyOf, isAsserted } from "../model/Assumption.js";
3
+ import { certaintyOf, idOf, isAsserted } from "../model/Assumption.js";
4
+ import { principalDerivationOf } from "../model/Version.js";
5
+ import { versionIn } from "../lookup/lookup.js";
6
+ import { witnessesOf } from "./witnesses.js";
4
7
  import { inMetersPerMinute, percent } from "../model/Quantity.js";
8
+ import { trackerBarOf } from "../systems/index.js";
5
9
  /**
6
10
  * How far the paper of one copy is taken to have stretched or shrunk
7
11
  * along the roll, as a standard deviation, where the edition has too few
@@ -258,3 +262,36 @@ export const alignmentProblems = (edition) => {
258
262
  });
259
263
  return [...unaligned, ...elsewhere, ...beyond, ...speeds];
260
264
  };
265
+ /**
266
+ * How long the version's paper runs for a length of the paper of the
267
+ * version it derives from, as the alignments give it: the ratio of the
268
+ * two versions' systems' papers, with its uncertainty. It is what the
269
+ * creation of the version states where the edition holds to it
270
+ * (`VersionCreation.lengthRatio`).
271
+ *
272
+ * The reading pools the copies of a system, so the ratio is only given
273
+ * for a version that one of those copies bears witness to: a second
274
+ * re-cut for the same system, whose copies measure no paper, is not
275
+ * credited with the first one's ratio. Where the reading has copies of
276
+ * two versions re-cut for one system, the ratio is theirs together.
277
+ * Nothing is given for a version that derives from none, or from one on
278
+ * its own system, whose paper it keeps.
279
+ */
280
+ export const lengthRatioOf = (edition, versionId) => {
281
+ const version = versionIn(edition, versionId);
282
+ const derivation = version && principalDerivationOf(version);
283
+ const base = derivation && versionIn(edition, idOf(derivation));
284
+ const reading = paperOf(edition);
285
+ if (!version || !base || !reading)
286
+ return undefined;
287
+ const systemOf = (of) => reading.systems.find(paper => paper.system === trackerBarOf(of.system)?.id);
288
+ const own = systemOf(version);
289
+ const theirs = systemOf(base);
290
+ if (!own || !theirs || own.system === theirs.system)
291
+ return undefined;
292
+ const witnesses = new Set(witnessesOf(edition, versionId).map(witness => witness.copy));
293
+ if (!own.copies.some(copy => witnesses.has(copy)))
294
+ return undefined;
295
+ const value = own.ratio / theirs.ratio;
296
+ return { value, uncertainty: value * Math.hypot(own.ratioError / own.ratio, theirs.ratioError / theirs.ratio) };
297
+ };
@@ -1,5 +1,5 @@
1
1
  import { bearsPhysicalEvidence, isMeasured, sourceLabels } from "../model/FeatureSource.js";
2
- import { calibrationOf, featuresOf } from "../model/RollCopy.js";
2
+ import { calibrationOf, featuresOf, measuringSoftwareOf } from "../model/RollCopy.js";
3
3
  import { trackerBarOf } from "../systems/index.js";
4
4
  import { witnessesOf } from "./witnesses.js";
5
5
  export const reservationTypes = [
@@ -49,7 +49,7 @@ const physicalEvidence = copy => copy.readFrom && !bearsPhysicalEvidence(copy.re
49
49
  type: 'no-physical-evidence',
50
50
  note: 'Punch diameter, hole separation, punching pattern, marks and writings cannot be observed on this source.'
51
51
  } : undefined;
52
- const measurementDocumented = copy => !copy.measurements.measuredBy && copy.readFrom?.kind !== 'roll' ? {
52
+ const measurementDocumented = copy => measuringSoftwareOf(copy).length === 0 && copy.readFrom?.kind !== 'roll' ? {
53
53
  type: 'measurement-undocumented',
54
54
  note: 'No measuring software is recorded for this copy.'
55
55
  } : undefined;
package/lib/io/migrate.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { v4 } from "uuid";
1
2
  import { systemIdIn, systemOf, translationBetween } from "../systems/TrackerBar.js";
2
3
  import { trackerBars } from "../systems/index.js";
3
4
  import { welteT100 } from "../systems/welteT100/bar.js";
@@ -451,9 +452,61 @@ const withTimeSpanDates = (node) => {
451
452
  const { '@value': within, '@type': _typed, ...rest } = node;
452
453
  return { ...rest, within };
453
454
  };
455
+ /** The values a copy's one `measuredBy` stood for, of those the edition exports: what the analysis it named had given. */
456
+ const measuredByCovered = ['dimensions', 'holeSeparation'];
457
+ /** A day written as a date, of a date written with or without its time. */
458
+ const dayOf = (date) => typeof date === 'string' && /^\d{4}-\d{2}-\d{2}/.test(date) ? date.slice(0, 10) : undefined;
459
+ /**
460
+ * A copy once named one program for all its measurements, beside values
461
+ * that could not say what took them. Each value the edition exports now
462
+ * carries the measurement it rests on, so the program, its version and
463
+ * the day go into the reasons of the dimensions and the hole separation,
464
+ * which the analysis it named had given. The scan resolution is read
465
+ * off the file rather than measured, and the margins are not exported;
466
+ * nothing is said of them. The ids are the copy's, so that migrating a
467
+ * file twice writes the same document.
468
+ */
469
+ const withMeasuredByInReasons = (node) => {
470
+ const measurements = node?.measurements;
471
+ if (!measurements || typeof measurements !== 'object' || !Object.hasOwn(measurements, 'measuredBy'))
472
+ return node;
473
+ const { measuredBy, ...rest } = measurements;
474
+ const day = dayOf(measuredBy?.date);
475
+ const reason = measuredBy?.software && {
476
+ '@type': 'measurement',
477
+ software: [{ name: measuredBy.software, ...(measuredBy.version && { version: measuredBy.version }) }],
478
+ ...(day && { date: { within: day } })
479
+ };
480
+ const owner = typeof node['@id'] === 'string' ? plainCopyId(node['@id']) : v4();
481
+ const withReason = (key, value) => {
482
+ if (!reason || !value || typeof value !== 'object')
483
+ return value;
484
+ const annotation = value['@annotation'];
485
+ if (annotation?.belief) {
486
+ return { ...value, '@annotation': { ...annotation, belief: { ...annotation.belief, reasons: [...(annotation.belief.reasons ?? []), reason] } } };
487
+ }
488
+ return {
489
+ ...value,
490
+ '@annotation': {
491
+ '@id': `${owner}-${key}-annotation`,
492
+ belief: { '@type': 'belief', '@id': `${owner}-${key}-belief`, certainty: 'true', reasons: [reason] }
493
+ }
494
+ };
495
+ };
496
+ return {
497
+ ...node,
498
+ measurements: {
499
+ ...rest,
500
+ ...Object.fromEntries(measuredByCovered
501
+ .filter(key => Object.hasOwn(rest, key))
502
+ .map(key => [key, withReason(key, rest[key])]))
503
+ }
504
+ };
505
+ };
454
506
  const nodeSteps = [withRenamedKeys, withRenamedEditType, withTypology, withRenamedType, withoutVersionType, withoutVersionSiglum, withLowerCaseTerms, withSplitMethod, withReferences, withKeeper, withoutEmptyKeeper,
455
507
  withPerforator, withProductionNodes, withTypedPerforator, withScale, withoutOps, withDerivationList, withReadingKind, withTimeSpanDates,
456
- withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withOwnPlaces, withDriveOfStaggering, withoutPattern, withoutBearings, withoutEntailedType];
508
+ withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withOwnPlaces, withDriveOfStaggering, withoutPattern, withoutBearings, withoutEntailedType,
509
+ withMeasuredByInReasons];
457
510
  const migrateNode = (node) => nodeSteps.reduce((result, step) => step(result), node);
458
511
  /** The value with every node in it migrated, a node before its children; the very same value where nothing changed. */
459
512
  const walk = (value) => {
@@ -78,4 +78,21 @@ export interface Place extends Named {
78
78
  * @see crm:E55 Type
79
79
  */
80
80
  export type Concept = (WithId & Partial<Named>) | (Named & Partial<WithId>);
81
+ /**
82
+ * Software that captured a copy or measured it: the transcriber that
83
+ * read a recording into notes, the emulator that wrote a MIDI file, the
84
+ * program that found the holes on a scan or aligned two copies.
85
+ * @see crmdig:D14 Software
86
+ */
87
+ export interface Software {
88
+ /**
89
+ * @see rdfs:label
90
+ */
91
+ name: string;
92
+ /**
93
+ * The release, the commit, or the model checkpoint of a transcriber.
94
+ * @see owl:versionInfo
95
+ */
96
+ version?: string;
97
+ }
81
98
  export {};
@@ -1,5 +1,5 @@
1
1
  import { WithNote, WithId } from "../shared/utils.js";
2
- import { Person, WithActor } from "./Agent.js";
2
+ import { Concept, Person, Software, WithActor } from "./Agent.js";
3
3
  export declare const certainties: readonly ['true', 'likely', 'possible', 'unlikely', 'false'];
4
4
  /**
5
5
  * Certainty levels for beliefs, ranging from 'true' to 'false'
@@ -102,6 +102,11 @@ export interface BeliefAdoption extends Argumentation<'beliefAdoption'> {
102
102
  * premises. CRMinf counts observations, and a measurement is one, among
103
103
  * the ways a belief comes about, so the value does not have to be
104
104
  * passed off as an inference.
105
+ *
106
+ * A measurement a program took names the program and its version, so
107
+ * that the value can be taken again and a value taken by a program
108
+ * later found wanting can be told from the rest. Naming software makes
109
+ * it a digital measurement event in the sense of CRMdig.
105
110
  * @see crmsci:S21 Measurement
106
111
  */
107
112
  export interface Measurement extends Argumentation<'measurement'> {
@@ -111,6 +116,24 @@ export interface Measurement extends Argumentation<'measurement'> {
111
116
  * @see crm:P16 used specific object
112
117
  */
113
118
  used?: string[];
119
+ /**
120
+ * The method followed, where it has a name apart from the program
121
+ * that carries it out, such as a line of Theil and Sen through the
122
+ * matched notes of two copies.
123
+ * @see crm:P33 used specific technique
124
+ */
125
+ procedure?: Concept;
126
+ /**
127
+ * The programs that took the measurement, each with its version.
128
+ * @see crmdig:L23 used software or firmware
129
+ */
130
+ software?: Software[];
131
+ /**
132
+ * When the measurement was taken. It is part of the reason and
133
+ * carries no belief of its own.
134
+ * @see crm:P4 has time-span
135
+ */
136
+ date?: TimeSpan;
114
137
  }
115
138
  /**
116
139
  * An argumentation can be either a plain argumentation, a
@@ -185,7 +208,6 @@ export type ObjectAssumption<O extends object> = Assumption & O;
185
208
  * annotated with a belief about its certainty.
186
209
  */
187
210
  export type ActorAssignment = ObjectAssumption<Person>;
188
- /** The day an event falls within. */
189
211
  interface DateWithin {
190
212
  /**
191
213
  * The day the event falls within.
@@ -194,8 +216,7 @@ interface DateWithin {
194
216
  */
195
217
  within: Date;
196
218
  }
197
- /** The bounds an event lies between. */
198
- interface DateBounds {
219
+ interface DateAfter {
199
220
  /**
200
221
  * The earliest day the event can have happened on: it did not
201
222
  * happen before it.
@@ -203,6 +224,22 @@ interface DateBounds {
203
224
  * @see crm:P82a begin of the begin
204
225
  */
205
226
  after: Date;
227
+ /**
228
+ * The latest day the event can have happened on: it did not
229
+ * happen after it.
230
+ * @format date
231
+ * @see crm:P82b end of the end
232
+ */
233
+ before?: Date;
234
+ }
235
+ interface DateBefore {
236
+ /**
237
+ * The earliest day the event can have happened on: it did not
238
+ * happen before it.
239
+ * @format date
240
+ * @see crm:P82a begin of the begin
241
+ */
242
+ after?: Date;
206
243
  /**
207
244
  * The latest day the event can have happened on: it did not
208
245
  * happen after it.
@@ -212,12 +249,20 @@ interface DateBounds {
212
249
  before: Date;
213
250
  }
214
251
  /**
215
- * When something happened, as far as the edition can state it: the day
216
- * it falls `within`, or the bounds it lies between. A bound nobody can
217
- * give is left out, so `after` alone says "not before".
252
+ * When something happened: the day it falls `within`, or the bounds it
253
+ * lies between. A bound nobody can give is left out, so `after` alone
254
+ * says "not before".
255
+ * @see crm:E52 Time-Span
256
+ */
257
+ export type TimeSpan = DateWithin | DateAfter | DateBefore;
258
+ /**
259
+ * When something happened, as far as the edition can state it, and as
260
+ * an assumption, so that a dating can carry the belief it rests on: the
261
+ * day it falls `within`, or the bounds it lies between. A bound nobody
262
+ * can give is left out, so `after` alone says "not before".
218
263
  * @see crm:E52 Time-Span
219
264
  */
220
- export type DateAssignment = Assumption & (DateWithin | (Pick<DateBounds, 'after'> & Partial<Pick<DateBounds, 'before'>>) | (Pick<DateBounds, 'before'> & Partial<Pick<DateBounds, 'after'>>));
265
+ export type DateAssignment = Assumption & TimeSpan;
221
266
  export declare function valueOf<ValueT>(assumption: ValueAssumption<ValueT>): ValueT;
222
267
  export declare function valuesOf<ValueT>(assumptions: ValueAssumption<ValueT>[]): ValueT[];
223
268
  export declare function idOf(assumption: ReferenceAssumption): string;
@@ -1,5 +1,5 @@
1
1
  import { ActorAssignment, DateAssignment, ObjectAssumption } from "./Assumption.js";
2
- import { Concept, Named } from "./Agent.js";
2
+ import { Concept, Named, Software } from "./Agent.js";
3
3
  import { ConditionState } from "./ConditionState.js";
4
4
  import { WithNote } from "../shared/utils.js";
5
5
  /**
@@ -25,22 +25,7 @@ export declare const isMeasured: (kind: SourceKind) => boolean;
25
25
  * be observed on the roll and on an image of it, and on nothing else.
26
26
  */
27
27
  export declare const bearsPhysicalEvidence: (kind: SourceKind) => boolean;
28
- /**
29
- * Software a capture ran, such as the transcriber that read a recording
30
- * into notes or the emulator that wrote a MIDI file.
31
- * @see crmdig:D14 Software
32
- */
33
- export interface Software {
34
- /**
35
- * @see rdfs:label
36
- */
37
- name: string;
38
- /**
39
- * The release, or the model checkpoint of a transcriber.
40
- * @see owl:versionInfo
41
- */
42
- version?: string;
43
- }
28
+ export type { Software } from "./Agent.js";
44
29
  /**
45
30
  * An instrument a copy was played on to be recorded. How it was
46
31
  * regulated, where that is known, is its condition.
@@ -5,7 +5,7 @@ import { TrackCalibration } from "./TrackCalibration.js";
5
5
  import { AnyFeature, FeatureOrPatch, HorizontalSpan, Patch } from "./Feature.js";
6
6
  import { ActorAssignment, DateAssignment, ObjectAssumption, ReferenceAssumption } from "./Assumption.js";
7
7
  import { WithId, WithType } from "../shared/utils.js";
8
- import { Agent, Concept } from "./Agent.js";
8
+ import { Agent, Concept, Software } from "./Agent.js";
9
9
  import { FeatureSource } from "./FeatureSource.js";
10
10
  import { Measure, Millimeters, Percent, Quantity, Track } from "./Quantity.js";
11
11
  import { Perforator } from "./Perforator.js";
@@ -161,6 +161,28 @@ export interface Alignment {
161
161
  date: Date;
162
162
  };
163
163
  }
164
+ /**
165
+ * The width and length of a roll copy's paper, as they were measured on
166
+ * it or on its scan.
167
+ * @see crm:E54 Dimension
168
+ */
169
+ export interface RollDimensions {
170
+ /**
171
+ * The width of the roll.
172
+ * @see reo:width
173
+ */
174
+ width: Millimeters;
175
+ /**
176
+ * The total height (length) of the roll.
177
+ * @see reo:height
178
+ */
179
+ height: Millimeters;
180
+ /**
181
+ * The unit of measurement.
182
+ * @see crm:P91 has unit
183
+ */
184
+ unit: 'mm';
185
+ }
164
186
  /** The margins on the treble and bass sides of the roll, in the unit the scan was measured in. */
165
187
  export interface Margins<U extends 'px' | 'mm'> {
166
188
  treble: Quantity<U>;
@@ -307,11 +329,14 @@ export type Modification = Partial<{
307
329
  */
308
330
  export interface RollCopy extends WithType<'RollCopy'>, WithId {
309
331
  /**
310
- * Physical measurements of this roll copy, including
311
- * dimensions, hole separation, margins, the alignment onto the
312
- * edition's axis, and information about the measuring software. What the
313
- * perforations tell about the machine that cut them is stated
314
- * with the production.
332
+ * Physical measurements of this roll copy: its dimensions, hole
333
+ * separation and margins, the resolution of its scan, and how it
334
+ * lies on the edition's axis. What the perforations tell about the
335
+ * machine that cut them is stated with the production.
336
+ *
337
+ * A value the edition exports is an object assumption, so that it
338
+ * can say what took it: a measurement naming the program and its
339
+ * version, the scan or analysis it was taken on, and the day.
315
340
  * @see crm:P39i was measured by
316
341
  */
317
342
  measurements: Partial<{
@@ -319,23 +344,7 @@ export interface RollCopy extends WithType<'RollCopy'>, WithId {
319
344
  * The physical dimensions of the roll.
320
345
  * @see reo:dimensions
321
346
  */
322
- dimensions: {
323
- /**
324
- * The width of the roll.
325
- * @see reo:width
326
- */
327
- width: Millimeters;
328
- /**
329
- * The total height (length) of the roll.
330
- * @see reo:height
331
- */
332
- height: Millimeters;
333
- /**
334
- * The unit of measurement.
335
- * @see crm:P91 has unit
336
- */
337
- unit: 'mm';
338
- };
347
+ dimensions: ObjectAssumption<RollDimensions>;
339
348
  /**
340
349
  * The distance across the roll from the centre of one track to
341
350
  * the next, the track pitch or Teilung, in the unit the scan was
@@ -344,7 +353,7 @@ export interface RollCopy extends WithType<'RollCopy'>, WithId {
344
353
  * along the roll.
345
354
  * @see reo:holeSeparation
346
355
  */
347
- holeSeparation: Measure<'px'> | Measure<'mm'>;
356
+ holeSeparation: ObjectAssumption<Measure<'px'> | Measure<'mm'>>;
348
357
  /**
349
358
  * The margins on the treble and bass sides of the roll.
350
359
  * Not exported to RDF.
@@ -389,35 +398,13 @@ export interface RollCopy extends WithType<'RollCopy'>, WithId {
389
398
  * files do not state.
390
399
  * @see reo:scanResolution
391
400
  */
392
- scanResolution: Measure<'px/in'>;
401
+ scanResolution: ObjectAssumption<Measure<'px/in'>>;
393
402
  /**
394
403
  * Relates this copy's scan to the tracker bar: how the scanning
395
404
  * software's hole numbering was shifted onto the bar, and where
396
405
  * the track grid sits in the image. Not exported to RDF.
397
406
  */
398
407
  trackCalibration: TrackCalibration;
399
- /**
400
- * Information about the software used to take the measurements.
401
- * @see crmdig:L23 used software or firmware
402
- */
403
- measuredBy: {
404
- /**
405
- * The name of the measurement software.
406
- * @see rdfs:label
407
- */
408
- software: string;
409
- /**
410
- * The version of the measurement software.
411
- * @see owl:versionInfo
412
- */
413
- version: string;
414
- /**
415
- * The date on which the measurements were taken.
416
- * @format date
417
- * @see dcterms:date
418
- */
419
- date: Date;
420
- };
421
408
  }>;
422
409
  /**
423
410
  * The production event that created this roll copy.
@@ -496,6 +483,8 @@ export declare const statesNothing: (modification: Modification) => boolean;
496
483
  export declare const featuresByAct: (copy: Pick<RollCopy, 'production' | 'modifications'>) => FeatureOrPatch[][];
497
484
  /** Every feature the copy states at a place of its own, whichever act made it. */
498
485
  export declare const featuresOf: (copy: Pick<RollCopy, 'production' | 'modifications'>) => FeatureOrPatch[];
486
+ /** The programs the measurements of a copy name as having taken them, each once, as far as they name any. */
487
+ export declare const measuringSoftwareOf: (copy: Pick<RollCopy, 'measurements'>) => Software[];
499
488
  /** Whether the condition is the stretch or shrinkage of the paper, as measured on the copy. */
500
489
  export declare const isPaperStretch: (condition: RollConditionAssignment) => condition is ObjectAssumption<PaperStretch>;
501
490
  /** What the copy's paper was measured to have done, where anything was measured. */
@@ -35,6 +35,16 @@ export const statesNothing = (modification) => modification.type === 'Removal' ?
35
35
  export const featuresByAct = (copy) => [copy.production?.produced ?? [], ...copy.modifications.map(featuresMadeBy)];
36
36
  /** Every feature the copy states at a place of its own, whichever act made it. */
37
37
  export const featuresOf = (copy) => featuresByAct(copy).flat();
38
+ /** The measurements of a copy the edition exports, each of which can say what took it. */
39
+ const exportedMeasurementsOf = (copy) => [copy.measurements.dimensions, copy.measurements.holeSeparation, copy.measurements.scanResolution]
40
+ .filter(value => value !== undefined);
41
+ /** The programs the measurements of a copy name as having taken them, each once, as far as they name any. */
42
+ export const measuringSoftwareOf = (copy) => {
43
+ const named = exportedMeasurementsOf(copy)
44
+ .flatMap(value => value['@annotation']?.belief.reasons ?? [])
45
+ .flatMap(reason => reason.type === 'measurement' ? reason.software ?? [] : []);
46
+ return [...new Map(named.map(software => [`${software.name}\u0000${software.version ?? ''}`, software])).values()];
47
+ };
38
48
  /** Whether the condition is the stretch or shrinkage of the paper, as measured on the copy. */
39
49
  export const isPaperStretch = (condition) => condition.conditionType === 'paper-stretch';
40
50
  /** What the copy's paper was measured to have done, where anything was measured. */
@@ -33,6 +33,23 @@ export type Derivation = ReferenceAssumption & {
33
33
  };
34
34
  /** The tolerance the derivation was collated at, or the default where it states none. */
35
35
  export declare const collationToleranceOf: (derivation: Readonly<Derivation>) => CollationTolerance;
36
+ /**
37
+ * How long a version's paper runs for a length of the paper of the
38
+ * version it derives from: about 0.77 for a Licensee or green re-cut of
39
+ * a red roll, cut for a slower paper speed.
40
+ * @see crm:E54 Dimension
41
+ */
42
+ export interface LengthRatio {
43
+ /**
44
+ * @see crm:P90 has value
45
+ */
46
+ value: number;
47
+ /**
48
+ * The standard uncertainty of the value, one standard deviation.
49
+ * @see reo:uncertainty
50
+ */
51
+ uncertainty?: number;
52
+ }
36
53
  /**
37
54
  * How a version was made, where that is known and worth stating.
38
55
  *
@@ -63,6 +80,19 @@ export interface VersionCreation {
63
80
  * @see crm:P33 used specific technique
64
81
  */
65
82
  procedure?: Concept;
83
+ /**
84
+ * How long the act made the version's paper for a length of the
85
+ * paper of the version it derives from. A re-cut for a system whose
86
+ * paper runs slower comes out shorter by the ratio of the speeds,
87
+ * and the alignments of the copies measure that ratio more closely
88
+ * than any label states a speed (`lengthRatioOf`). It is stated
89
+ * where the edition holds to it, as a measurement naming the copies
90
+ * it used and the program that found it, so that it can serve as a
91
+ * premise. A speed follows from it only together with a premise
92
+ * about the speed of the other version.
93
+ * @see reo:lengthRatio
94
+ */
95
+ lengthRatio?: ObjectAssumption<LengthRatio>;
66
96
  }
67
97
  /**
68
98
  * A version is defined by the sum of edits applied to the version it is
@@ -1,5 +1,6 @@
1
1
  import { v4 } from "uuid";
2
2
  import { AtonParser } from "./AtonParser.js";
3
+ import { assignDate } from "../model/Assumption.js";
3
4
  import { systemOf } from "../systems/TrackerBar.js";
4
5
  import { welteT100 } from "../systems/welteT100/bar.js";
5
6
  import { inMillimeters, mean, mm, pixelsPerInch, px, subtract, track } from "../model/Quantity.js";
@@ -120,19 +121,24 @@ const tearsIn = (tears, dpi, depictionOf) => {
120
121
  .sort((a, b) => a.horizontal.from - b.horizontal.from);
121
122
  };
122
123
  /**
123
- * The software behind an analysis and when it was run, as the
124
- * analysis file states them.
124
+ * The software behind an analysis and the day it was run, as the
125
+ * analysis file states them: the measurement the dimensions and the
126
+ * hole separation it gives rest on.
125
127
  */
126
- const measuredByOf = (rollinfo) => {
128
+ const measurementOf = (rollinfo) => {
127
129
  const date = new Date(rollinfo.ANALYSIS_DATE);
128
130
  if (!rollinfo.HOLE_SOFTWARE || isNaN(date.getTime()))
129
131
  return undefined;
130
132
  return {
131
- software: rollinfo.HOLE_SOFTWARE,
132
- version: rollinfo.SOFTWARE_DATE ?? '',
133
- date
133
+ type: 'measurement',
134
+ software: [{ name: rollinfo.HOLE_SOFTWARE, ...(rollinfo.SOFTWARE_DATE && { version: rollinfo.SOFTWARE_DATE }) }],
135
+ date: assignDate(new Date(date.getFullYear(), date.getMonth(), date.getDate()))
134
136
  };
135
137
  };
138
+ /** The value held on the strength of the measurement, where the file names one, and plainly where it does not. */
139
+ const takenBy = (measurement) => (value) => measurement
140
+ ? { ...value, '@annotation': { id: v4(), belief: { type: 'belief', id: v4(), certainty: 'true', reasons: [measurement] } } }
141
+ : value;
136
142
  export function readFromStanfordAton(atonString, { trackShift, system = welteT100, scan } = {}) {
137
143
  const parser = new AtonParser();
138
144
  const json = parser.parse(atonString);
@@ -141,7 +147,7 @@ export function readFromStanfordAton(atonString, { trackShift, system = welteT10
141
147
  const stanford = druid ? stanfordScan(druid) : undefined;
142
148
  const separation = readPx(json.ROLLINFO.HOLE_SEPARATION);
143
149
  const dpi = parseFloat(json.ROLLINFO.LENGTH_DPI);
144
- const measuredBy = measuredByOf(json.ROLLINFO);
150
+ const measured = takenBy(measurementOf(json.ROLLINFO));
145
151
  const shift = trackShift ?? calibrationShiftIn(holes, system);
146
152
  const calibration = {
147
153
  unit: 'px',
@@ -196,15 +202,15 @@ export function readFromStanfordAton(atonString, { trackShift, system = welteT10
196
202
  modifications: [],
197
203
  ...((scan ?? stanford) && { scan: scan ?? stanford?.scan }),
198
204
  measurements: {
199
- dimensions: {
205
+ dimensions: measured({
200
206
  width: inMillimeters(readPx(json.ROLLINFO.ROLL_WIDTH), dpi),
201
207
  height: inMillimeters(readPx(json.ROLLINFO.IMAGE_LENGTH), dpi),
202
208
  unit: 'mm'
203
- },
204
- holeSeparation: {
209
+ }),
210
+ holeSeparation: measured({
205
211
  value: separation,
206
212
  unit: 'px'
207
- },
213
+ }),
208
214
  margins: {
209
215
  treble: readPx(json.ROLLINFO.HARD_MARGIN_TREBLE),
210
216
  bass: readPx(json.ROLLINFO.HARD_MARGIN_BASS),
@@ -213,8 +219,7 @@ export function readFromStanfordAton(atonString, { trackShift, system = welteT10
213
219
  ...(Number.isFinite(dpi) && {
214
220
  scanResolution: { value: pixelsPerInch(dpi), unit: 'px/in' }
215
221
  }),
216
- trackCalibration: calibration,
217
- ...(measuredBy && { measuredBy })
222
+ trackCalibration: calibration
218
223
  }
219
224
  };
220
225
  }
package/lib/schema.json CHANGED
@@ -449,7 +449,7 @@
449
449
  "type": "object"
450
450
  }
451
451
  ],
452
- "description": "When something happened, as far as the edition can state it: the day it falls `within`, or the bounds it lies between. A bound nobody can give is left out, so `after` alone says \"not before\".",
452
+ "description": "When something happened, as far as the edition can state it, and as an assumption, so that a dating can carry the belief it rests on: the day it falls `within`, or the bounds it lies between. A bound nobody can give is left out, so `after` alone says \"not before\".",
453
453
  "ontology": "crm:E52 Time-Span"
454
454
  },
455
455
  "Derivation": {
@@ -1145,63 +1145,37 @@
1145
1145
  "type": "object",
1146
1146
  "ontology": "crm:E54 Dimension"
1147
1147
  },
1148
- "Measure<\"px\">": {
1149
- "description": "A value together with the unit it was measured in, as a record states it.",
1150
- "properties": {
1151
- "unit": {
1152
- "const": "px",
1153
- "description": "The unit of measurement.",
1154
- "type": "string",
1155
- "ontology": "crm:P91 has unit"
1156
- },
1157
- "value": {
1158
- "$ref": "#/definitions/Quantity%3C%22px%22%3E",
1159
- "description": "The measured value.",
1160
- "ontology": "crm:P90 has value"
1161
- }
1162
- },
1163
- "required": [
1164
- "value",
1165
- "unit"
1166
- ],
1167
- "type": "object",
1168
- "ontology": "crm:E54 Dimension"
1169
- },
1170
- "Measure<\"px/in\">": {
1171
- "description": "A value together with the unit it was measured in, as a record states it.",
1172
- "properties": {
1173
- "unit": {
1174
- "const": "px/in",
1175
- "description": "The unit of measurement.",
1176
- "type": "string",
1177
- "ontology": "crm:P91 has unit"
1178
- },
1179
- "value": {
1180
- "$ref": "#/definitions/Quantity%3C%22px%2Fin%22%3E",
1181
- "description": "The measured value.",
1182
- "ontology": "crm:P90 has value"
1183
- }
1184
- },
1185
- "required": [
1186
- "value",
1187
- "unit"
1188
- ],
1189
- "type": "object",
1190
- "ontology": "crm:E54 Dimension"
1191
- },
1192
1148
  "Measurement": {
1193
- "description": "A measurement takes a value off an object by a stated procedure: the period the slot lengths of a copy keep, the median pitch of its held notes. It concludes the belief in that value by itself, with no premises. CRMinf counts observations, and a measurement is one, among the ways a belief comes about, so the value does not have to be passed off as an inference.",
1149
+ "description": "A measurement takes a value off an object by a stated procedure: the period the slot lengths of a copy keep, the median pitch of its held notes. It concludes the belief in that value by itself, with no premises. CRMinf counts observations, and a measurement is one, among the ways a belief comes about, so the value does not have to be passed off as an inference.\n\nA measurement a program took names the program and its version, so that the value can be taken again and a value taken by a program later found wanting can be told from the rest. Naming software makes it a digital measurement event in the sense of CRMdig.",
1194
1150
  "properties": {
1195
1151
  "actor": {
1196
1152
  "$ref": "#/definitions/Person",
1197
1153
  "description": "The person who carried out this activity.",
1198
1154
  "ontology": "crm:P14 carried out by"
1199
1155
  },
1156
+ "date": {
1157
+ "$ref": "#/definitions/TimeSpan",
1158
+ "description": "When the measurement was taken. It is part of the reason and carries no belief of its own.",
1159
+ "ontology": "crm:P4 has time-span"
1160
+ },
1200
1161
  "note": {
1201
1162
  "description": "A free-text note providing additional context.",
1202
1163
  "type": "string",
1203
1164
  "ontology": "crm:P3 has note"
1204
1165
  },
1166
+ "procedure": {
1167
+ "$ref": "#/definitions/Concept",
1168
+ "description": "The method followed, where it has a name apart from the program that carries it out, such as a line of Theil and Sen through the matched notes of two copies.",
1169
+ "ontology": "crm:P33 used specific technique"
1170
+ },
1171
+ "software": {
1172
+ "description": "The programs that took the measurement, each with its version.",
1173
+ "items": {
1174
+ "$ref": "#/definitions/Software"
1175
+ },
1176
+ "type": "array",
1177
+ "ontology": "crmdig:L23 used software or firmware"
1178
+ },
1205
1179
  "used": {
1206
1180
  "description": "References (by `@id` or URL) to what the measurement was taken on or with, such as the scan, the analysis output or the script.",
1207
1181
  "items": {
@@ -1872,6 +1846,89 @@
1872
1846
  ],
1873
1847
  "description": "An object assumption wraps a complex object with an optional annotation. Used for structured values (e.g. persons, conditions) whose properties may be uncertain."
1874
1848
  },
1849
+ "ObjectAssumption<(Measure<\"px\">|Measure<\"mm\">)>": {
1850
+ "anyOf": [
1851
+ {
1852
+ "properties": {
1853
+ "@annotation": {
1854
+ "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.",
1855
+ "properties": {
1856
+ "belief": {
1857
+ "$ref": "#/definitions/Belief",
1858
+ "description": "The belief held about the annotated statement.",
1859
+ "ontology": "crminf:J4i is subject of"
1860
+ },
1861
+ "@id": {
1862
+ "description": "A unique identifier for this object.",
1863
+ "type": "string"
1864
+ }
1865
+ },
1866
+ "required": [
1867
+ "belief",
1868
+ "@id"
1869
+ ],
1870
+ "type": "object"
1871
+ },
1872
+ "unit": {
1873
+ "const": "px",
1874
+ "description": "The unit of measurement.",
1875
+ "type": "string",
1876
+ "ontology": "crm:P91 has unit"
1877
+ },
1878
+ "value": {
1879
+ "$ref": "#/definitions/Quantity%3C%22px%22%3E",
1880
+ "description": "The measured value.",
1881
+ "ontology": "crm:P90 has value"
1882
+ }
1883
+ },
1884
+ "required": [
1885
+ "unit",
1886
+ "value"
1887
+ ],
1888
+ "type": "object"
1889
+ },
1890
+ {
1891
+ "properties": {
1892
+ "@annotation": {
1893
+ "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.",
1894
+ "properties": {
1895
+ "belief": {
1896
+ "$ref": "#/definitions/Belief",
1897
+ "description": "The belief held about the annotated statement.",
1898
+ "ontology": "crminf:J4i is subject of"
1899
+ },
1900
+ "@id": {
1901
+ "description": "A unique identifier for this object.",
1902
+ "type": "string"
1903
+ }
1904
+ },
1905
+ "required": [
1906
+ "belief",
1907
+ "@id"
1908
+ ],
1909
+ "type": "object"
1910
+ },
1911
+ "unit": {
1912
+ "const": "mm",
1913
+ "description": "The unit of measurement.",
1914
+ "type": "string",
1915
+ "ontology": "crm:P91 has unit"
1916
+ },
1917
+ "value": {
1918
+ "$ref": "#/definitions/Quantity%3C%22mm%22%3E",
1919
+ "description": "The measured value.",
1920
+ "ontology": "crm:P90 has value"
1921
+ }
1922
+ },
1923
+ "required": [
1924
+ "unit",
1925
+ "value"
1926
+ ],
1927
+ "type": "object"
1928
+ }
1929
+ ],
1930
+ "description": "An object assumption wraps a complex object with an optional annotation. Used for structured values (e.g. persons, conditions) whose properties may be uncertain."
1931
+ },
1875
1932
  "ObjectAssumption<Advance>": {
1876
1933
  "description": "An object assumption wraps a complex object with an optional annotation. Used for structured values (e.g. persons, conditions) whose properties may be uncertain.",
1877
1934
  "properties": {
@@ -2458,6 +2515,43 @@
2458
2515
  ],
2459
2516
  "type": "object"
2460
2517
  },
2518
+ "ObjectAssumption<LengthRatio>": {
2519
+ "description": "An object assumption wraps a complex object with an optional annotation. Used for structured values (e.g. persons, conditions) whose properties may be uncertain.",
2520
+ "properties": {
2521
+ "@annotation": {
2522
+ "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.",
2523
+ "properties": {
2524
+ "belief": {
2525
+ "$ref": "#/definitions/Belief",
2526
+ "description": "The belief held about the annotated statement.",
2527
+ "ontology": "crminf:J4i is subject of"
2528
+ },
2529
+ "@id": {
2530
+ "description": "A unique identifier for this object.",
2531
+ "type": "string"
2532
+ }
2533
+ },
2534
+ "required": [
2535
+ "belief",
2536
+ "@id"
2537
+ ],
2538
+ "type": "object"
2539
+ },
2540
+ "uncertainty": {
2541
+ "description": "The standard uncertainty of the value, one standard deviation.",
2542
+ "type": "number",
2543
+ "ontology": "reo:uncertainty"
2544
+ },
2545
+ "value": {
2546
+ "type": "number",
2547
+ "ontology": "crm:P90 has value"
2548
+ }
2549
+ },
2550
+ "required": [
2551
+ "value"
2552
+ ],
2553
+ "type": "object"
2554
+ },
2461
2555
  "ObjectAssumption<Measure<\"mm\">>": {
2462
2556
  "description": "An object assumption wraps a complex object with an optional annotation. Used for structured values (e.g. persons, conditions) whose properties may be uncertain.",
2463
2557
  "properties": {
@@ -2498,6 +2592,46 @@
2498
2592
  ],
2499
2593
  "type": "object"
2500
2594
  },
2595
+ "ObjectAssumption<Measure<\"px/in\">>": {
2596
+ "description": "An object assumption wraps a complex object with an optional annotation. Used for structured values (e.g. persons, conditions) whose properties may be uncertain.",
2597
+ "properties": {
2598
+ "@annotation": {
2599
+ "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.",
2600
+ "properties": {
2601
+ "belief": {
2602
+ "$ref": "#/definitions/Belief",
2603
+ "description": "The belief held about the annotated statement.",
2604
+ "ontology": "crminf:J4i is subject of"
2605
+ },
2606
+ "@id": {
2607
+ "description": "A unique identifier for this object.",
2608
+ "type": "string"
2609
+ }
2610
+ },
2611
+ "required": [
2612
+ "belief",
2613
+ "@id"
2614
+ ],
2615
+ "type": "object"
2616
+ },
2617
+ "unit": {
2618
+ "const": "px/in",
2619
+ "description": "The unit of measurement.",
2620
+ "type": "string",
2621
+ "ontology": "crm:P91 has unit"
2622
+ },
2623
+ "value": {
2624
+ "$ref": "#/definitions/Quantity%3C%22px%2Fin%22%3E",
2625
+ "description": "The measured value.",
2626
+ "ontology": "crm:P90 has value"
2627
+ }
2628
+ },
2629
+ "required": [
2630
+ "unit",
2631
+ "value"
2632
+ ],
2633
+ "type": "object"
2634
+ },
2501
2635
  "ObjectAssumption<PaperSpeed>": {
2502
2636
  "anyOf": [
2503
2637
  {
@@ -2635,6 +2769,52 @@
2635
2769
  ],
2636
2770
  "type": "object"
2637
2771
  },
2772
+ "ObjectAssumption<RollDimensions>": {
2773
+ "description": "An object assumption wraps a complex object with an optional annotation. Used for structured values (e.g. persons, conditions) whose properties may be uncertain.",
2774
+ "properties": {
2775
+ "@annotation": {
2776
+ "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.",
2777
+ "properties": {
2778
+ "belief": {
2779
+ "$ref": "#/definitions/Belief",
2780
+ "description": "The belief held about the annotated statement.",
2781
+ "ontology": "crminf:J4i is subject of"
2782
+ },
2783
+ "@id": {
2784
+ "description": "A unique identifier for this object.",
2785
+ "type": "string"
2786
+ }
2787
+ },
2788
+ "required": [
2789
+ "belief",
2790
+ "@id"
2791
+ ],
2792
+ "type": "object"
2793
+ },
2794
+ "height": {
2795
+ "$ref": "#/definitions/Millimeters",
2796
+ "description": "The total height (length) of the roll.",
2797
+ "ontology": "reo:height"
2798
+ },
2799
+ "unit": {
2800
+ "const": "mm",
2801
+ "description": "The unit of measurement.",
2802
+ "type": "string",
2803
+ "ontology": "crm:P91 has unit"
2804
+ },
2805
+ "width": {
2806
+ "$ref": "#/definitions/Millimeters",
2807
+ "description": "The width of the roll.",
2808
+ "ontology": "reo:width"
2809
+ }
2810
+ },
2811
+ "required": [
2812
+ "height",
2813
+ "unit",
2814
+ "width"
2815
+ ],
2816
+ "type": "object"
2817
+ },
2638
2818
  "ObjectAssumption<RollTempo>": {
2639
2819
  "anyOf": [
2640
2820
  {
@@ -3153,49 +3333,19 @@
3153
3333
  "ontology": "crm:P50 has current keeper"
3154
3334
  },
3155
3335
  "measurements": {
3156
- "description": "Physical measurements of this roll copy, including dimensions, hole separation, margins, the alignment onto the edition's axis, and information about the measuring software. What the perforations tell about the machine that cut them is stated with the production.",
3336
+ "description": "Physical measurements of this roll copy: its dimensions, hole separation and margins, the resolution of its scan, and how it lies on the edition's axis. What the perforations tell about the machine that cut them is stated with the production.\n\nA value the edition exports is an object assumption, so that it can say what took it: a measurement naming the program and its version, the scan or analysis it was taken on, and the day.",
3157
3337
  "properties": {
3158
3338
  "alignment": {
3159
3339
  "$ref": "#/definitions/Alignment",
3160
3340
  "description": "How the copy's places are carried onto the edition's axis. Left out for the reference copy, whose places are the axis, and for a copy not aligned yet. Not exported to RDF."
3161
3341
  },
3162
3342
  "dimensions": {
3343
+ "$ref": "#/definitions/ObjectAssumption%3CRollDimensions%3E",
3163
3344
  "description": "The physical dimensions of the roll.",
3164
- "properties": {
3165
- "height": {
3166
- "$ref": "#/definitions/Millimeters",
3167
- "description": "The total height (length) of the roll.",
3168
- "ontology": "reo:height"
3169
- },
3170
- "unit": {
3171
- "const": "mm",
3172
- "description": "The unit of measurement.",
3173
- "type": "string",
3174
- "ontology": "crm:P91 has unit"
3175
- },
3176
- "width": {
3177
- "$ref": "#/definitions/Millimeters",
3178
- "description": "The width of the roll.",
3179
- "ontology": "reo:width"
3180
- }
3181
- },
3182
- "required": [
3183
- "width",
3184
- "height",
3185
- "unit"
3186
- ],
3187
- "type": "object",
3188
3345
  "ontology": "reo:dimensions"
3189
3346
  },
3190
3347
  "holeSeparation": {
3191
- "anyOf": [
3192
- {
3193
- "$ref": "#/definitions/Measure%3C%22px%22%3E"
3194
- },
3195
- {
3196
- "$ref": "#/definitions/Measure%3C%22mm%22%3E"
3197
- }
3198
- ],
3348
+ "$ref": "#/definitions/ObjectAssumption%3C(Measure%3C%22px%22%3E%7CMeasure%3C%22mm%22%3E)%3E",
3199
3349
  "description": "The distance across the roll from the centre of one track to the next, the track pitch or Teilung, in the unit the scan was measured in. It is fixed by the tracker bar the roll was cut for, and is another thing than the chain pitch, which runs along the roll.",
3200
3350
  "ontology": "reo:holeSeparation"
3201
3351
  },
@@ -3210,34 +3360,6 @@
3210
3360
  ],
3211
3361
  "description": "The margins on the treble and bass sides of the roll. Not exported to RDF."
3212
3362
  },
3213
- "measuredBy": {
3214
- "description": "Information about the software used to take the measurements.",
3215
- "properties": {
3216
- "date": {
3217
- "description": "The date on which the measurements were taken.",
3218
- "format": "date",
3219
- "type": "string",
3220
- "ontology": "dcterms:date"
3221
- },
3222
- "software": {
3223
- "description": "The name of the measurement software.",
3224
- "type": "string",
3225
- "ontology": "rdfs:label"
3226
- },
3227
- "version": {
3228
- "description": "The version of the measurement software.",
3229
- "type": "string",
3230
- "ontology": "owl:versionInfo"
3231
- }
3232
- },
3233
- "required": [
3234
- "software",
3235
- "version",
3236
- "date"
3237
- ],
3238
- "type": "object",
3239
- "ontology": "crmdig:L23 used software or firmware"
3240
- },
3241
3363
  "readerExtension": {
3242
3364
  "description": "What a pneumatic reader added to this copy's chains of holes, where it was taken off again. Such a reader reports how long a valve stayed open, which exceeds the perforation that opened it, so its chains are overlong by a constant while its onsets agree. Nothing for a copy read by other means, whose chains are the punched ones themselves. Not exported to RDF.",
3243
3365
  "properties": {
@@ -3259,7 +3381,7 @@
3259
3381
  "type": "object"
3260
3382
  },
3261
3383
  "scanResolution": {
3262
- "$ref": "#/definitions/Measure%3C%22px%2Fin%22%3E",
3384
+ "$ref": "#/definitions/ObjectAssumption%3CMeasure%3C%22px%2Fin%22%3E%3E",
3263
3385
  "description": "The resolution this copy's scan was read at, along the roll. It is what the measurements given in pixels are to be read against, and what every conversion between a place in the scan and one on the paper goes through. A scanner may read across the roll at another resolution, which the analysis files do not state.",
3264
3386
  "ontology": "reo:scanResolution"
3265
3387
  },
@@ -3346,14 +3468,14 @@
3346
3468
  "type": "object"
3347
3469
  },
3348
3470
  "Software": {
3349
- "description": "Software a capture ran, such as the transcriber that read a recording into notes or the emulator that wrote a MIDI file.",
3471
+ "description": "Software that captured a copy or measured it: the transcriber that read a recording into notes, the emulator that wrote a MIDI file, the program that found the holes on a scan or aligned two copies.",
3350
3472
  "properties": {
3351
3473
  "name": {
3352
3474
  "type": "string",
3353
3475
  "ontology": "rdfs:label"
3354
3476
  },
3355
3477
  "version": {
3356
- "description": "The release, or the model checkpoint of a transcriber.",
3478
+ "description": "The release, the commit, or the model checkpoint of a transcriber.",
3357
3479
  "type": "string",
3358
3480
  "ontology": "owl:versionInfo"
3359
3481
  }
@@ -3447,6 +3569,66 @@
3447
3569
  "type": "object",
3448
3570
  "ontology": "crm:E33 Linguistic Object"
3449
3571
  },
3572
+ "TimeSpan": {
3573
+ "anyOf": [
3574
+ {
3575
+ "properties": {
3576
+ "within": {
3577
+ "description": "The day the event falls within.",
3578
+ "format": "date",
3579
+ "type": "string",
3580
+ "ontology": "crm:P82 at some time within"
3581
+ }
3582
+ },
3583
+ "required": [
3584
+ "within"
3585
+ ],
3586
+ "type": "object"
3587
+ },
3588
+ {
3589
+ "properties": {
3590
+ "after": {
3591
+ "description": "The earliest day the event can have happened on: it did not happen before it.",
3592
+ "format": "date",
3593
+ "type": "string",
3594
+ "ontology": "crm:P82a begin of the begin"
3595
+ },
3596
+ "before": {
3597
+ "description": "The latest day the event can have happened on: it did not happen after it.",
3598
+ "format": "date",
3599
+ "type": "string",
3600
+ "ontology": "crm:P82b end of the end"
3601
+ }
3602
+ },
3603
+ "required": [
3604
+ "after"
3605
+ ],
3606
+ "type": "object"
3607
+ },
3608
+ {
3609
+ "properties": {
3610
+ "after": {
3611
+ "description": "The earliest day the event can have happened on: it did not happen before it.",
3612
+ "format": "date",
3613
+ "type": "string",
3614
+ "ontology": "crm:P82a begin of the begin"
3615
+ },
3616
+ "before": {
3617
+ "description": "The latest day the event can have happened on: it did not happen after it.",
3618
+ "format": "date",
3619
+ "type": "string",
3620
+ "ontology": "crm:P82b end of the end"
3621
+ }
3622
+ },
3623
+ "required": [
3624
+ "before"
3625
+ ],
3626
+ "type": "object"
3627
+ }
3628
+ ],
3629
+ "description": "When something happened: the day it falls `within`, or the bounds it lies between. A bound nobody can give is left out, so `after` alone says \"not before\".",
3630
+ "ontology": "crm:E52 Time-Span"
3631
+ },
3450
3632
  "Track": {
3451
3633
  "$ref": "#/definitions/Quantity%3C%22track%22%3E",
3452
3634
  "description": "A position across the roll, numbered as the tracker bar does."
@@ -3541,6 +3723,11 @@
3541
3723
  "description": "When it took place.",
3542
3724
  "ontology": "crm:P4 has time-span"
3543
3725
  },
3726
+ "lengthRatio": {
3727
+ "$ref": "#/definitions/ObjectAssumption%3CLengthRatio%3E",
3728
+ "description": "How long the act made the version's paper for a length of the paper of the version it derives from. A re-cut for a system whose paper runs slower comes out shorter by the ratio of the speeds, and the alignments of the copies measure that ratio more closely than any label states a speed (`lengthRatioOf`). It is stated where the edition holds to it, as a measurement naming the copies it used and the program that found it, so that it can serve as a premise. A speed follows from it only together with a premise about the speed of the other version.",
3729
+ "ontology": "reo:lengthRatio"
3730
+ },
3544
3731
  "procedure": {
3545
3732
  "$ref": "#/definitions/Concept",
3546
3733
  "description": "The rule followed, as a term of the vocabulary: the notes stand at their pitch, the expression is re-spelled in the other system's words.",
@@ -89,6 +89,10 @@
89
89
  "used": {
90
90
  "@id": "crm:P16_used_specific_object",
91
91
  "@type": "@id"
92
+ },
93
+ "software": {
94
+ "@id": "crmdig:L23_used_software_or_firmware",
95
+ "@context": { "version": "owl:versionInfo" }
92
96
  }
93
97
  }
94
98
  },
@@ -99,6 +103,13 @@
99
103
  },
100
104
  "creation": "lrmoo:R17i_was_created_by",
101
105
  "procedure": "crm:P33_used_specific_technique",
106
+ "lengthRatio": {
107
+ "@id": "reo:lengthRatio",
108
+ "@context": {
109
+ "value": "crm:P90_has_value",
110
+ "uncertainty": "reo:uncertainty"
111
+ }
112
+ },
102
113
  "editors": "crm:P14_carried_out_by",
103
114
  "publisher": "crm:P14_carried_out_by",
104
115
  "publicationDate": {
@@ -176,14 +187,7 @@
176
187
  "height": "reo:height",
177
188
  "holeSeparation": "reo:holeSeparation",
178
189
  "scanResolution": "reo:scanResolution",
179
- "value": "crm:P90_has_value",
180
- "measuredBy": "crmdig:L23_used_software_or_firmware",
181
- "software": "rdfs:label",
182
- "version": "owl:versionInfo",
183
- "date": {
184
- "@id": "dcterms:date",
185
- "@type": "xsd:date"
186
- }
190
+ "value": "crm:P90_has_value"
187
191
  }
188
192
  },
189
193
  "production": {
@@ -67,12 +67,15 @@ export declare const pedalPresetOf: (pedals: Parameters) => PedalPreset | undefi
67
67
  *
68
68
  * Rows are rows of the version's own paper, so the spool is asked in
69
69
  * `paperOfRow` and never in `placeOfRow`: one is the paper that passes the
70
- * tracker bar, the other is the coordinate the edition states.
70
+ * tracker bar, the other is the coordinate the edition states. A place is
71
+ * put to the spool through `paperOfPlace` for the same reason, so that a
72
+ * note sounds on the clock its pedal and bellows run on.
71
73
  */
72
74
  export type Paper = {
73
75
  readonly rowOf: (place: Millimeters) => number;
74
76
  readonly placeOfRow: (row: number) => Millimeters;
75
77
  readonly paperOfRow: (row: number) => Millimeters;
78
+ readonly paperOfPlace: (place: Millimeters) => Millimeters;
76
79
  };
77
80
  /** When the spool brings a place on the roll to the tracker bar. */
78
81
  export declare const secondsAt: (spool: Spool, place: Millimeters) => Seconds;
@@ -28,7 +28,8 @@ const isExpression = (event) => event.type === 'expression';
28
28
  const paperOf = (toOwnPaper) => ({
29
29
  rowOf: place => place * toOwnPaper * ROWS_PER_MM,
30
30
  placeOfRow: row => mm(row / (toOwnPaper * ROWS_PER_MM)),
31
- paperOfRow: row => mm(row / ROWS_PER_MM)
31
+ paperOfRow: row => mm(row / ROWS_PER_MM),
32
+ paperOfPlace: place => mm(place * toOwnPaper)
32
33
  });
33
34
  /** When the spool brings a place on the roll to the tracker bar. */
34
35
  export const secondsAt = (spool, place) => seconds(paperSeconds(spool, inCentimeters(place)));
@@ -67,9 +68,10 @@ const performNotes = (events, grid, nuance, options, paper) => events
67
68
  .flatMap((note) => {
68
69
  const curve = nuance[halfOf(note, options.division)];
69
70
  const velocity = curve.velocity[grid.indexOfRow(paper.rowOf(note.horizontal.from))];
71
+ const at = (place) => secondsAt(options.spool, paper.paperOfPlace(place));
70
72
  return [
71
- { type: 'noteOn', performs: note, pitch: note.pitch, velocity, at: secondsAt(options.spool, note.horizontal.from) },
72
- { type: 'noteOff', performs: note, pitch: note.pitch, velocity: 127, at: secondsAt(options.spool, note.horizontal.to) }
73
+ { type: 'noteOn', performs: note, pitch: note.pitch, velocity, at: at(note.horizontal.from) },
74
+ { type: 'noteOff', performs: note, pitch: note.pitch, velocity: 127, at: at(note.horizontal.to) }
73
75
  ];
74
76
  });
75
77
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linked-rolls",
3
- "version": "0.65.0",
3
+ "version": "0.66.0",
4
4
  "description": "Digital editions of piano rolls: import, collation, editorial assumptions, JSON-LD export, and emulation through a reproducing system",
5
5
  "license": "MIT",
6
6
  "repository": {