linked-rolls 0.38.0 → 0.39.1

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
@@ -78,12 +78,17 @@ derivation loads as a list of one.
78
78
  ## What brought a feature about
79
79
 
80
80
  A copy states no list of features. Each feature stands in the act that
81
- brought it about: the perforations in the copy's own production, a
82
- writing or a mark in the `Alteration` that made it, a patch in the
83
- `Attachment` that glued it on. A `Removal` names by id what was taken
84
- off, which stood there before the act. A feature no later act is known
85
- to have made belongs to the punching, which is where a reading of a
86
- scan puts every hole it finds.
81
+ brought it about: the copy's own production for what the punching made,
82
+ an `Alteration` for what a later hand made, an `Attachment` for a patch
83
+ glued on. A `Removal` names by id what was taken off, which stood there
84
+ before the act.
85
+
86
+ Which act a feature stands in says when it came about and by whose
87
+ hand, and says nothing about what kind of feature it is. A hole may be
88
+ punched by hand long after the roll was cut, and a mark may come from
89
+ the factory, rarely though that happens. A feature no later act is
90
+ known to have made belongs to the punching, which is where a reading of
91
+ a scan puts every hole it finds.
87
92
 
88
93
  Three kinds of act rather than two, because the CRM tells them apart.
89
94
  E79 Part Addition asks that what is added be "a separate identifiable
package/lib/Feature.d.ts CHANGED
@@ -2,7 +2,7 @@ import { ObjectAssumption } from "./Assumption";
2
2
  import { ConditionState } from "./ConditionState";
3
3
  import { Text } from "./Symbol";
4
4
  import { PartialBy, WithId, WithType } from "./utils";
5
- import { Millimeters, Track } from "./Quantity";
5
+ import { Measure, Millimeters, Track } from "./Quantity";
6
6
  /**
7
7
  * Describes the horizontal extent of a feature on the roll,
8
8
  * measured in millimeters from the beginning of the roll.
@@ -119,11 +119,23 @@ export interface Hole extends RollFeature<'Hole', typeof conditions.Hole[number]
119
119
  */
120
120
  pattern?: 'regular' | 'accelerating' | 'staggering';
121
121
  }
122
+ /**
123
+ * Something that lies on a face of the paper rather than through it. A
124
+ * hole is not one: it goes through, and is on both faces at once.
125
+ */
126
+ export interface OnAFace {
127
+ /**
128
+ * The face of the roll it lies on: the side that faces the reader
129
+ * as the roll plays, or the back of it.
130
+ * @see reo:side
131
+ */
132
+ side?: 'recto' | 'verso';
133
+ }
122
134
  /**
123
135
  * A trace is a visible mark or writing on the roll surface.
124
136
  * Traces may fade over time.
125
137
  */
126
- export interface Trace<T extends FeatureType> extends RollFeature<T, typeof conditions[T][number]> {
138
+ export interface Trace<T extends FeatureType> extends RollFeature<T, typeof conditions[T][number]>, OnAFace {
127
139
  }
128
140
  export declare const writingMethods: readonly ['print', 'handwriting', 'stamp'];
129
141
  /**
@@ -156,6 +168,15 @@ export interface Writing extends Trace<'Writing'> {
156
168
  * @see crm:P128 carries
157
169
  */
158
170
  transcription: ObjectAssumption<Transcription>;
171
+ /**
172
+ * How far the writing stands askew. At zero it reads across the
173
+ * roll, from the bass side towards the treble, and the angle turns
174
+ * that reading direction towards the end of the roll, so that a
175
+ * right angle leaves it reading along the roll. A stamp pressed
176
+ * crooked and a label written lengthwise are both stated here.
177
+ * @see reo:rotation
178
+ */
179
+ rotation?: Measure<'deg'>;
159
180
  }
160
181
  /**
161
182
  * A visible mark on the roll, such as a pencil circle, an ink stroke,
@@ -178,12 +199,19 @@ export interface Mark extends Trace<'Mark'> {
178
199
  * such as writings or additional holes.
179
200
  * @see reo:GluedOn
180
201
  */
181
- export interface GluedOn extends RollFeature<'GluedOn', typeof conditions.GluedOn[number]> {
202
+ export interface GluedOn extends RollFeature<'GluedOn', typeof conditions.GluedOn[number]>, OnAFace {
182
203
  /**
183
204
  * The material of the glued-on feature.
184
205
  * @see crm:P45 consists of
185
206
  */
186
207
  material: 'paper' | 'tape';
208
+ /**
209
+ * How far the patch was stuck on askew. At zero its edges stand
210
+ * square to the roll, and the angle turns them towards the end of
211
+ * it, as a writing's reading direction turns.
212
+ * @see reo:rotation
213
+ */
214
+ rotation?: Measure<'deg'>;
187
215
  /**
188
216
  * A glued-on feature itself may carry other features.
189
217
  * Nested features do not need to be positioned explicitly.
package/lib/Quantity.d.ts CHANGED
@@ -11,7 +11,7 @@ declare const unit: unique symbol;
11
11
  * The units a record may state. A `Measure` is limited to these, since
12
12
  * they are what the `unit` field is allowed to say.
13
13
  */
14
- export type Unit = 'mm' | 'cm' | 'px' | 'track' | 's' | 'ms' | 'ft/min' | 'm/min' | 'px/in';
14
+ export type Unit = 'mm' | 'cm' | 'px' | 'track' | 's' | 'ms' | 'ft/min' | 'm/min' | 'px/in' | 'deg';
15
15
  export type Quantity<U extends string> = number & {
16
16
  readonly [unit]: U;
17
17
  };
@@ -28,6 +28,8 @@ export type FeetPerMinute = Quantity<'ft/min'>;
28
28
  export type MetersPerMinute = Quantity<'m/min'>;
29
29
  /** How finely a scan was read: pixels of the image per inch of paper. */
30
30
  export type Resolution = Quantity<'px/in'>;
31
+ /** An angle on the roll, turning from the line across the paper towards the end of the roll. */
32
+ export type Degrees = Quantity<'deg'>;
31
33
  /** Names a number in a unit. Partially apply it to make a constructor. */
32
34
  export declare const quantity: <U extends string>(value: number) => Quantity<U>;
33
35
  export declare const mm: (value: number) => Quantity<"mm">;
@@ -39,6 +41,7 @@ export declare const milliseconds: (value: number) => Quantity<"ms">;
39
41
  export declare const feetPerMinute: (value: number) => Quantity<"ft/min">;
40
42
  export declare const metersPerMinute: (value: number) => Quantity<"m/min">;
41
43
  export declare const pixelsPerInch: (value: number) => Quantity<"px/in">;
44
+ export declare const degrees: (value: number) => Quantity<"deg">;
42
45
  /**
43
46
  * A value together with the unit it was measured in, as a record
44
47
  * states it.
package/lib/Quantity.js CHANGED
@@ -9,6 +9,7 @@ export const milliseconds = quantity;
9
9
  export const feetPerMinute = quantity;
10
10
  export const metersPerMinute = quantity;
11
11
  export const pixelsPerInch = quantity;
12
+ export const degrees = quantity;
12
13
  // The second operand takes its unit from the first. Inferred from both,
13
14
  // TypeScript would unite two units rather than reject them.
14
15
  export const add = (a, b) => quantity(a + b);
package/lib/RollCopy.d.ts CHANGED
@@ -115,10 +115,12 @@ export interface ProductionEvent {
115
115
  */
116
116
  speed?: ObjectAssumption<PaperSpeed>;
117
117
  /**
118
- * The features the punching brought into being, which are the note
119
- * and expression perforations. A reading of a scan finds every hole
120
- * on the paper at once and states it here. A hole an editor reads as
121
- * punched later belongs in the act that made it.
118
+ * The features the copy came from its punching with: the note and
119
+ * expression perforations, and now and then a mark the perforator
120
+ * left. A reading of a scan finds every hole on the paper at once
121
+ * and states it here, and a feature an editor reads as the work of
122
+ * a later hand belongs in the act that made it, whichever kind of
123
+ * feature it is.
122
124
  * @see crm:P108 has produced
123
125
  */
124
126
  produced?: AnyFeature[];
@@ -161,7 +163,9 @@ export type Modification = Partial<{
161
163
  type: 'Alteration';
162
164
  /**
163
165
  * The features the act brought into being: a date written on the
164
- * paper, a circle in pencil, a hole punched by hand.
166
+ * paper, a circle in pencil, a hole punched by hand. Any kind of
167
+ * feature may come of a later act, as any kind may come of the
168
+ * punching.
165
169
  * @see crm:P108 has produced
166
170
  */
167
171
  produced: AnyFeature[];
@@ -77,7 +77,7 @@ export declare const symbolsCarriedOnlyBy: (edition: Edition, copyId: string) =>
77
77
  * punched, being glued on, so `punched` says nothing of one.
78
78
  */
79
79
  export interface FeatureAct {
80
- /** The copy was punched with it, which is where a reading of a scan puts every hole. */
80
+ /** The copy came from its punching with it, which is where a reading of a scan puts every hole. */
81
81
  punched?: boolean;
82
82
  /** The act that brought this feature about brought the new one about as well. */
83
83
  beside?: string;
@@ -123,6 +123,14 @@ export type MergeObstacle = 'fewer-than-two' | 'different-types' | 'different-tr
123
123
  * perforations of their own is the editor's reading.
124
124
  */
125
125
  export declare const mergeObstacle: (features: readonly FeatureOrPatch[]) => MergeObstacle | undefined;
126
+ /**
127
+ * What stands in the way of reading the features the ids name as one,
128
+ * as `mergeFeatures` will find it. `mergeObstacle` asks only what the
129
+ * features themselves say, which cannot reach `different-acts`: one
130
+ * feature is the work of one act, and where the features stand is the
131
+ * edition's business rather than theirs.
132
+ */
133
+ export declare const mergeObstacleIn: (view: EditionView, featureIds: readonly string[]) => MergeObstacle | undefined;
126
134
  /**
127
135
  * Replaces the features of the copy with a single one covering them
128
136
  * all, where a scan has split what the editor reads as one feature.
package/lib/editionOps.js CHANGED
@@ -511,6 +511,32 @@ export const mergeObstacle = (features) => {
511
511
  return 'differing-conditions';
512
512
  return undefined;
513
513
  };
514
+ /** The acts of the copy that state any of the named features. */
515
+ const actsHolding = (copy, named) => featuresByAct(copy).filter(features => features.some(feature => named.has(feature.id)));
516
+ /**
517
+ * What stands in the way of reading the named features of the copy as
518
+ * one, which is the whole of what `mergeFeatures` asks: whether one act
519
+ * brought them all about, and what the features themselves say. An id
520
+ * the copy does not bear at a place of its own is passed over.
521
+ */
522
+ const obstacleIn = (copy, featureIds) => {
523
+ const named = new Set(featureIds);
524
+ const acts = actsHolding(copy, named);
525
+ return acts.length > 1
526
+ ? 'different-acts'
527
+ : mergeObstacle((acts[0] ?? []).filter(feature => named.has(feature.id)));
528
+ };
529
+ /**
530
+ * What stands in the way of reading the features the ids name as one,
531
+ * as `mergeFeatures` will find it. `mergeObstacle` asks only what the
532
+ * features themselves say, which cannot reach `different-acts`: one
533
+ * feature is the work of one act, and where the features stand is the
534
+ * edition's business rather than theirs.
535
+ */
536
+ export const mergeObstacleIn = (view, featureIds) => {
537
+ const copy = featureIds.map(id => view.copyOf(id)).find(copy => copy !== undefined);
538
+ return copy ? obstacleIn(copy, featureIds) : 'fewer-than-two';
539
+ };
514
540
  /** The items with the replacement in the place of the first it stands for, the others dropped. */
515
541
  const standingFor = (items, stands, replacement) => {
516
542
  const first = items.findIndex(stands);
@@ -576,14 +602,13 @@ const carryOver = (draft, replaced, mergedId) => {
576
602
  * says beforehand whether they can.
577
603
  */
578
604
  export const mergeFeatures = (copyId, featureIds) => onCopy(copyId, (copy, draft) => {
579
- const named = new Set(featureIds);
580
- const holds = (features) => features.some(feature => named.has(feature.id));
581
- const acts = featuresByAct(stateOf(copy)).filter(holds);
582
- const toMerge = (acts[0] ?? []).filter(feature => named.has(feature.id));
583
- const obstacle = acts.length > 1 ? 'different-acts' : mergeObstacle(toMerge);
605
+ const state = stateOf(copy);
606
+ const obstacle = obstacleIn(state, featureIds);
584
607
  if (obstacle) {
585
608
  throw new Error(`The features of copy ${copyId} cannot be merged: ${obstacle}`);
586
609
  }
610
+ const named = new Set(featureIds);
611
+ const toMerge = (actsHolding(state, named)[0] ?? []).filter(feature => named.has(feature.id));
587
612
  const replaced = new Set(toMerge.map(feature => feature.id));
588
613
  const mergedId = v4();
589
614
  rewriteFeatures(copy, (stated) => {
package/lib/schema.json CHANGED
@@ -780,6 +780,20 @@
780
780
  "type": "string",
781
781
  "ontology": "crm:P45 consists of"
782
782
  },
783
+ "rotation": {
784
+ "$ref": "#/definitions/Measure%3C%22deg%22%3E",
785
+ "description": "How far the patch was stuck on askew. At zero its edges stand square to the roll, and the angle turns them towards the end of it, as a writing's reading direction turns.",
786
+ "ontology": "reo:rotation"
787
+ },
788
+ "side": {
789
+ "description": "The face of the roll it lies on: the side that faces the reader as the roll plays, or the back of it.",
790
+ "enum": [
791
+ "recto",
792
+ "verso"
793
+ ],
794
+ "type": "string",
795
+ "ontology": "reo:side"
796
+ },
783
797
  "vertical": {
784
798
  "$ref": "#/definitions/VerticalSpan",
785
799
  "description": "Vertical span of the feature on the roll.",
@@ -1015,6 +1029,15 @@
1015
1029
  "description": "The medium the mark was drawn in, where it can be told.",
1016
1030
  "ontology": "reo:method"
1017
1031
  },
1032
+ "side": {
1033
+ "description": "The face of the roll it lies on: the side that faces the reader as the roll plays, or the back of it.",
1034
+ "enum": [
1035
+ "recto",
1036
+ "verso"
1037
+ ],
1038
+ "type": "string",
1039
+ "ontology": "reo:side"
1040
+ },
1018
1041
  "vertical": {
1019
1042
  "$ref": "#/definitions/VerticalSpan",
1020
1043
  "description": "Vertical span of the feature on the roll.",
@@ -1084,6 +1107,28 @@
1084
1107
  "type": "object",
1085
1108
  "ontology": "crminf:I16 Meaning Comprehension"
1086
1109
  },
1110
+ "Measure<\"deg\">": {
1111
+ "description": "A value together with the unit it was measured in, as a record states it.",
1112
+ "properties": {
1113
+ "unit": {
1114
+ "const": "deg",
1115
+ "description": "The unit of measurement.",
1116
+ "type": "string",
1117
+ "ontology": "crm:P91 has unit"
1118
+ },
1119
+ "value": {
1120
+ "$ref": "#/definitions/Quantity%3C%22deg%22%3E",
1121
+ "description": "The measured value.",
1122
+ "ontology": "crm:P90 has value"
1123
+ }
1124
+ },
1125
+ "required": [
1126
+ "value",
1127
+ "unit"
1128
+ ],
1129
+ "type": "object",
1130
+ "ontology": "crm:E54 Dimension"
1131
+ },
1087
1132
  "Measure<\"mm\">": {
1088
1133
  "description": "A value together with the unit it was measured in, as a record states it.",
1089
1134
  "properties": {
@@ -1219,7 +1264,7 @@
1219
1264
  "ontology": "crm:P4 has time-span"
1220
1265
  },
1221
1266
  "produced": {
1222
- "description": "The features the act brought into being: a date written on the paper, a circle in pencil, a hole punched by hand.",
1267
+ "description": "The features the act brought into being: a date written on the paper, a circle in pencil, a hole punched by hand. Any kind of feature may come of a later act, as any kind may come of the punching.",
1223
1268
  "items": {
1224
1269
  "$ref": "#/definitions/AnyFeature"
1225
1270
  },
@@ -1411,6 +1456,20 @@
1411
1456
  "description": "The method by which this writing was produced (e.g. through print, handwriting, or stamping).",
1412
1457
  "ontology": "reo:method"
1413
1458
  },
1459
+ "rotation": {
1460
+ "$ref": "#/definitions/Measure%3C%22deg%22%3E",
1461
+ "description": "How far the writing stands askew. At zero it reads across the roll, from the bass side towards the treble, and the angle turns that reading direction towards the end of the roll, so that a right angle leaves it reading along the roll. A stamp pressed crooked and a label written lengthwise are both stated here.",
1462
+ "ontology": "reo:rotation"
1463
+ },
1464
+ "side": {
1465
+ "description": "The face of the roll it lies on: the side that faces the reader as the roll plays, or the back of it.",
1466
+ "enum": [
1467
+ "recto",
1468
+ "verso"
1469
+ ],
1470
+ "type": "string",
1471
+ "ontology": "reo:side"
1472
+ },
1414
1473
  "transcription": {
1415
1474
  "$ref": "#/definitions/ObjectAssumption%3CTranscription%3E",
1416
1475
  "description": "A transcription of the text content of the writing. This is an object assumption so that the transcription can be annotated with a belief about its correctness.",
@@ -1462,6 +1521,15 @@
1462
1521
  "description": "The medium the mark was drawn in, where it can be told.",
1463
1522
  "ontology": "reo:method"
1464
1523
  },
1524
+ "side": {
1525
+ "description": "The face of the roll it lies on: the side that faces the reader as the roll plays, or the back of it.",
1526
+ "enum": [
1527
+ "recto",
1528
+ "verso"
1529
+ ],
1530
+ "type": "string",
1531
+ "ontology": "reo:side"
1532
+ },
1465
1533
  "vertical": {
1466
1534
  "$ref": "#/definitions/VerticalSpan",
1467
1535
  "description": "Vertical span of the feature on the roll. Left out by a feature a patch bears.",
@@ -1518,6 +1586,20 @@
1518
1586
  "type": "string",
1519
1587
  "ontology": "crm:P45 consists of"
1520
1588
  },
1589
+ "rotation": {
1590
+ "$ref": "#/definitions/Measure%3C%22deg%22%3E",
1591
+ "description": "How far the patch was stuck on askew. At zero its edges stand square to the roll, and the angle turns them towards the end of it, as a writing's reading direction turns.",
1592
+ "ontology": "reo:rotation"
1593
+ },
1594
+ "side": {
1595
+ "description": "The face of the roll it lies on: the side that faces the reader as the roll plays, or the back of it.",
1596
+ "enum": [
1597
+ "recto",
1598
+ "verso"
1599
+ ],
1600
+ "type": "string",
1601
+ "ontology": "reo:side"
1602
+ },
1521
1603
  "vertical": {
1522
1604
  "$ref": "#/definitions/VerticalSpan",
1523
1605
  "description": "Vertical span of the feature on the roll. Left out by a feature a patch bears.",
@@ -2385,7 +2467,7 @@
2385
2467
  "ontology": "crm:P126 employed"
2386
2468
  },
2387
2469
  "produced": {
2388
- "description": "The features the punching brought into being, which are the note and expression perforations. A reading of a scan finds every hole on the paper at once and states it here. A hole an editor reads as punched later belongs in the act that made it.",
2470
+ "description": "The features the copy came from its punching with: the note and expression perforations, and now and then a mark the perforator left. A reading of a scan finds every hole on the paper at once and states it here, and a feature an editor reads as the work of a later hand belongs in the act that made it, whichever kind of feature it is.",
2389
2471
  "items": {
2390
2472
  "$ref": "#/definitions/AnyFeature"
2391
2473
  },
@@ -2406,6 +2488,9 @@
2406
2488
  "type": "object",
2407
2489
  "ontology": "lrmoo:F32 Item Production Event"
2408
2490
  },
2491
+ "Quantity<\"deg\">": {
2492
+ "type": "number"
2493
+ },
2409
2494
  "Quantity<\"ft/min\">": {
2410
2495
  "type": "number"
2411
2496
  },
@@ -3005,6 +3090,20 @@
3005
3090
  "description": "The method by which this writing was produced (e.g. through print, handwriting, or stamping).",
3006
3091
  "ontology": "reo:method"
3007
3092
  },
3093
+ "rotation": {
3094
+ "$ref": "#/definitions/Measure%3C%22deg%22%3E",
3095
+ "description": "How far the writing stands askew. At zero it reads across the roll, from the bass side towards the treble, and the angle turns that reading direction towards the end of the roll, so that a right angle leaves it reading along the roll. A stamp pressed crooked and a label written lengthwise are both stated here.",
3096
+ "ontology": "reo:rotation"
3097
+ },
3098
+ "side": {
3099
+ "description": "The face of the roll it lies on: the side that faces the reader as the roll plays, or the back of it.",
3100
+ "enum": [
3101
+ "recto",
3102
+ "verso"
3103
+ ],
3104
+ "type": "string",
3105
+ "ontology": "reo:side"
3106
+ },
3008
3107
  "transcription": {
3009
3108
  "$ref": "#/definitions/ObjectAssumption%3CTranscription%3E",
3010
3109
  "description": "A transcription of the text content of the writing. This is an object assumption so that the transcription can be annotated with a belief about its correctness.",
@@ -247,6 +247,15 @@
247
247
  "@context": { "@vocab": "https://w3id.org/reo/type/" }
248
248
  },
249
249
  "transcription": "crm:P128_carries",
250
+ "side": {
251
+ "@id": "reo:side",
252
+ "@type": "@vocab",
253
+ "@context": { "@vocab": "https://w3id.org/reo/type/" }
254
+ },
255
+ "rotation": {
256
+ "@id": "reo:rotation",
257
+ "@context": { "value": "crm:P90_has_value" }
258
+ },
250
259
  "material": {
251
260
  "@id": "crm:P45_consists_of",
252
261
  "@type": "@vocab",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linked-rolls",
3
- "version": "0.38.0",
3
+ "version": "0.39.1",
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": {