@muhammara/native-core 7.0.0-beta.3 → 7.0.0-rc.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.
Files changed (43) hide show
  1. package/README.md +57 -7
  2. package/THIRD_PARTY_NOTICES.md +39 -0
  3. package/index.js +120 -1
  4. package/lib/PDFRStreamForBuffer.js +52 -7
  5. package/lib/PDFRStreamForFile.js +56 -7
  6. package/lib/PDFStreamForResponse.js +22 -5
  7. package/lib/PDFWStreamForBuffer.js +49 -10
  8. package/lib/PDFWStreamForFile.js +26 -5
  9. package/lib/Recipe.js +86 -30
  10. package/lib/content-stream.js +503 -0
  11. package/lib/css-colors.js +149 -0
  12. package/lib/font-text.js +1461 -0
  13. package/lib/glyph-list.js +1127 -0
  14. package/lib/recipe/annotation.js +482 -105
  15. package/lib/recipe/appendPage.js +8 -1
  16. package/lib/recipe/colors.js +132 -35
  17. package/lib/recipe/coordinate.js +82 -16
  18. package/lib/recipe/encrypt.js +32 -13
  19. package/lib/recipe/font.js +70 -25
  20. package/lib/recipe/graphics-state.js +3 -2
  21. package/lib/recipe/htmlToTextObjects.js +154 -21
  22. package/lib/recipe/image.js +34 -9
  23. package/lib/recipe/info.js +93 -22
  24. package/lib/recipe/insertPage.js +12 -3
  25. package/lib/recipe/overlay.js +4 -2
  26. package/lib/recipe/page-references.js +436 -0
  27. package/lib/recipe/page.js +379 -275
  28. package/lib/recipe/parameters.js +11 -2
  29. package/lib/recipe/replaceText.js +464 -30
  30. package/lib/recipe/shapes.js +418 -45
  31. package/lib/recipe/split.js +6 -2
  32. package/lib/recipe/table.js +260 -173
  33. package/lib/recipe/text.helper.js +176 -10
  34. package/lib/recipe/text.js +420 -115
  35. package/lib/recipe/utils.js +66 -3
  36. package/lib/recipe/vector-line.js +101 -39
  37. package/lib/recipe/vector-polygon.js +18 -6
  38. package/lib/recipe/vector.helper.js +150 -13
  39. package/lib/recipe/vector.js +150 -53
  40. package/lib/recipe/xObjectForm.js +73 -10
  41. package/lib/recipe-constants.js +373 -0
  42. package/muhammara.d.ts +3487 -152
  43. package/package.json +1 -1
@@ -1,3 +1,18 @@
1
+ const {
2
+ TriangleTrait,
3
+ TrianglePosition,
4
+ ArrowAt,
5
+ ArrowType,
6
+ } = require("../recipe-constants");
7
+
8
+ // Corners of an arrow shaft: bottom/top, left/right.
9
+ const ShaftCorner = Object.freeze({
10
+ BOTTOM_RIGHT: "br",
11
+ BOTTOM_LEFT: "bl",
12
+ TOP_LEFT: "tl",
13
+ TOP_RIGHT: "tr",
14
+ });
15
+
1
16
  /* N-Gon border box for odd numbered side shapes used to deal with object rotation.
2
17
 
3
18
  -------------------------------------------------------------- =========
@@ -53,23 +68,56 @@ to produce a final border box with a center that coincides with n-gon center.
53
68
 
54
69
  */
55
70
 
71
+ /**
72
+ * Whether a number is odd.
73
+ * @private
74
+ * @param {number} n - The number.
75
+ * @returns {boolean} True when n is odd.
76
+ */
56
77
  function odd(n) {
57
78
  return n % 2 !== 0;
58
79
  }
59
80
 
81
+ /**
82
+ * Convert degrees to radians.
83
+ * @private
84
+ * @param {number} angle - The angle in degrees.
85
+ * @returns {number} The angle in radians.
86
+ */
60
87
  function toRadians(angle) {
61
88
  return angle * (Math.PI / 180);
62
89
  }
63
90
 
91
+ /**
92
+ * Convert radians to degrees.
93
+ * @private
94
+ * @param {number} radians - The angle in radians.
95
+ * @returns {number} The angle in degrees.
96
+ */
64
97
  function toDegrees(radians) {
65
98
  return radians * (180 / Math.PI);
66
99
  }
67
100
 
101
+ /**
102
+ * The end point of a segment from a start point, length and angle.
103
+ * @private
104
+ * @param {number} x - The start x.
105
+ * @param {number} y - The start y.
106
+ * @param {number} l - The segment length.
107
+ * @param {number} angle - The direction in degrees.
108
+ * @returns {number[]} The end point [x, y].
109
+ */
68
110
  function endPoint(x, y, l, angle) {
69
111
  const radians = toRadians(angle);
70
112
  return [x + l * Math.cos(radians), y + l * Math.sin(radians)];
71
113
  }
72
114
 
115
+ /**
116
+ * The bounding box of a list of points.
117
+ * @private
118
+ * @param {number[][]} coords - The [x, y] points; at least one.
119
+ * @returns {number[]} [minX, minY, maxX, maxY].
120
+ */
73
121
  function boundingBox(coords) {
74
122
  let boundBox = [coords[0][0], coords[0][1], coords[0][0], coords[0][1]];
75
123
  for (const coord of coords) {
@@ -81,6 +129,38 @@ function boundingBox(coords) {
81
129
  return boundBox;
82
130
  }
83
131
 
132
+ // More vertices than this cannot be told apart from a circle, and an
133
+ // unbounded count builds a vertex array until the process runs out of memory.
134
+ const MAX_POLYGON_VERTICES = 100000;
135
+
136
+ /**
137
+ * Validate a polygon side or star point count.
138
+ * @private
139
+ * @param {*} count - The requested count.
140
+ * @param {string} name - The argument name used in the error, such as "n_gon sides".
141
+ * @throws {RangeError} If the count is not a finite number or exceeds the maximum.
142
+ */
143
+ function _checkVertexCount(count, name) {
144
+ const value = Number(count);
145
+ if (!Number.isFinite(value) || value > MAX_POLYGON_VERTICES) {
146
+ throw new RangeError(
147
+ `${name} must be a finite number no greater than ${MAX_POLYGON_VERTICES}`,
148
+ );
149
+ }
150
+ }
151
+
152
+ /**
153
+ * The vertices of a regular polygon, inset by half the line width so the
154
+ * stroke stays inside the radius. Sets `options.deltaYY` for odd-sided
155
+ * rotation, and `options.rotationOrigin` when `rotationVertice` is given.
156
+ * @private
157
+ * @param {number} sides - The number of sides.
158
+ * @param {number} cx - The center x.
159
+ * @param {number} cy - The center y.
160
+ * @param {number} radius - The distance from the center to a vertex.
161
+ * @param {Object} [options] - The n_gon() options; updated in place.
162
+ * @returns {number[][]} The [x, y] vertices.
163
+ */
84
164
  function _n_gon(sides, cx, cy, radius, options = {}) {
85
165
  let lineWidth = 0;
86
166
 
@@ -125,7 +205,8 @@ function _n_gon(sides, cx, cy, radius, options = {}) {
125
205
  * @param {number} cx - x-coordinate of center point of regular polygon
126
206
  * @param {number} cy - y-coordinate of center point of regular polygon
127
207
  * @param {number} radius - The radius, distance from the center of the polygon to a vertice.
128
- * @param {number} [sides=3] - the number of sides of the regular polygon
208
+ * @param {number|Object} [sides=3] - the number of sides of the regular polygon, at least 3;
209
+ * or the options when the side count is omitted.
129
210
  * @param {Object} [options] - The options
130
211
  * @param {string|number[]} [options.color] - HexColor or DecimalColor
131
212
  * @param {string|number[]} [options.stroke] - HexColor or DecimalColor
@@ -138,7 +219,11 @@ function _n_gon(sides, cx, cy, radius, options = {}) {
138
219
  * @param {number} [options.rotationVertice] - the number of the vertice to be used as rotation origin
139
220
  * @param {number} [options.skewX] - the angle skew off the x-axis
140
221
  * @param {number} [options.skewY] - the angle skew off the y-axis.
222
+ * @param {string} [options.link] - Make the polygon's bounding square open this URL.
223
+ * @param {boolean} [options.debug] - Also draw the circumscribed circle and center.
141
224
  * @returns {Recipe} The recipe instance.
225
+ * @throws {TypeError} If no page is active.
226
+ * @throws {RangeError} If `sides` is not a finite number or exceeds 100000.
142
227
  */
143
228
  exports.n_gon = function n_gon(cx, cy, radius, sides = 3, options = {}) {
144
229
  const MIN_SIDES = 3;
@@ -149,6 +234,7 @@ exports.n_gon = function n_gon(cx, cy, radius, sides = 3, options = {}) {
149
234
  sides = MIN_SIDES;
150
235
  }
151
236
 
237
+ _checkVertexCount(sides, "n_gon sides");
152
238
  if (sides < MIN_SIDES) {
153
239
  sides = MIN_SIDES;
154
240
  }
@@ -172,6 +258,12 @@ exports.n_gon = function n_gon(cx, cy, radius, sides = 3, options = {}) {
172
258
  return this;
173
259
  };
174
260
 
261
+ /**
262
+ * Reorder polygon vertices into a star path by skipping vertices.
263
+ * @private
264
+ * @param {number[][]} ngon - The polygon vertices.
265
+ * @returns {number[][]} The star path.
266
+ */
175
267
  function _oddStar(ngon) {
176
268
  let starPath = [];
177
269
  let points = ngon.length;
@@ -192,7 +284,8 @@ function _oddStar(ngon) {
192
284
  * @param {number} cx - x-coordinate of center point of regular polygon
193
285
  * @param {number} cy - y-coordinate of center point of regular polygon
194
286
  * @param {number} radius - The radius, distance from the center to a star point.
195
- * @param {number} [points=5] - number of points on star
287
+ * @param {number|Object} [points=5] - number of points on star, at least 5; or
288
+ * the options when the point count is omitted.
196
289
  * @param {Object} [options] - The options
197
290
  * @param {string|number[]} [options.color] - HexColor or DecimalColor
198
291
  * @param {string|number[]} [options.stroke] - HexColor or DecimalColor
@@ -204,7 +297,11 @@ function _oddStar(ngon) {
204
297
  * @param {number[]} [options.rotationOrigin] - [originX, originY] Default: x, y
205
298
  * @param {number} [options.skewX] - the angle skew off the x-axis
206
299
  * @param {number} [options.skewY] - the angle skew off the y-axis.
300
+ * @param {string} [options.link] - Make the star's bounding square open this URL.
301
+ * @param {boolean} [options.debug] - Also draw the circumscribed circle and center.
207
302
  * @returns {Recipe} The recipe instance.
303
+ * @throws {TypeError} If no page is active.
304
+ * @throws {RangeError} If `points` is not a finite number or exceeds 100000.
208
305
  */
209
306
  exports.star = function star(cx, cy, radius, points = 5, options = {}) {
210
307
  let starPath = [];
@@ -218,6 +315,7 @@ exports.star = function star(cx, cy, radius, points = 5, options = {}) {
218
315
  points = MIN_POINTS;
219
316
  }
220
317
 
318
+ _checkVertexCount(points, "star points");
221
319
  if (points < MIN_POINTS) {
222
320
  points = MIN_POINTS;
223
321
  }
@@ -280,6 +378,16 @@ exports.star = function star(cx, cy, radius, points = 5, options = {}) {
280
378
  return this;
281
379
  };
282
380
 
381
+ /**
382
+ * Rotate a point around an origin.
383
+ * @private
384
+ * @param {number} ox - The point x.
385
+ * @param {number} oy - The point y.
386
+ * @param {number} p - The origin x.
387
+ * @param {number} q - The origin y.
388
+ * @param {number} angle - The rotation in degrees.
389
+ * @returns {number[]} The rotated [x, y].
390
+ */
283
391
  function rotate(ox, oy, p, q, angle) {
284
392
  let [x, y] = [ox, oy];
285
393
  angle = angle % 360; // keep angle within realistic bounds
@@ -312,6 +420,12 @@ function rotate(ox, oy, p, q, angle) {
312
420
  return [x, y];
313
421
  }
314
422
 
423
+ /**
424
+ * The center of the bounding box of a list of points.
425
+ * @private
426
+ * @param {number[][]} ngon - The [x, y] points.
427
+ * @returns {number[]} The center [x, y].
428
+ */
315
429
  function center(ngon) {
316
430
  let [minX, minY, maxX, maxY] = boundingBox(ngon);
317
431
  let width = maxX - minX;
@@ -319,6 +433,14 @@ function center(ngon) {
319
433
  return [minX + width / 2, minY + height / 2];
320
434
  }
321
435
 
436
+ /**
437
+ * Move points by an offset. The point arrays are changed in place.
438
+ * @private
439
+ * @param {number} dx - The x offset.
440
+ * @param {number} dy - The y offset.
441
+ * @param {number[][]} ngon - The [x, y] points.
442
+ * @returns {number[][]} A new list holding the moved points.
443
+ */
322
444
  function translate(dx, dy, ngon) {
323
445
  let object = ngon.slice();
324
446
  for (const coord of object) {
@@ -329,6 +451,13 @@ function translate(dx, dy, ngon) {
329
451
  return object;
330
452
  }
331
453
 
454
+ /**
455
+ * Mirror points across a horizontal line. The point arrays are changed in place.
456
+ * @private
457
+ * @param {number} y - The y of the mirror line.
458
+ * @param {number[][]} ngon - The [x, y] points.
459
+ * @returns {number[][]} A new list holding the mirrored points.
460
+ */
332
461
  function flipX(y, ngon) {
333
462
  let object = ngon.slice();
334
463
  for (const coord of object) {
@@ -338,6 +467,13 @@ function flipX(y, ngon) {
338
467
  return object;
339
468
  }
340
469
 
470
+ /**
471
+ * Mirror points across a vertical line. The point arrays are changed in place.
472
+ * @private
473
+ * @param {number} x - The x of the mirror line.
474
+ * @param {number[][]} ngon - The [x, y] points.
475
+ * @returns {number[][]} A new list holding the mirrored points.
476
+ */
341
477
  function flipY(x, ngon) {
342
478
  let object = ngon.slice();
343
479
  for (const coord of object) {
@@ -356,10 +492,12 @@ function flipY(x, ngon) {
356
492
  * @param {number} y - y-coordinate used to position triangle, by default associated with left vertex of triangle base.
357
493
  * @param {number[]} traits - the data defining the triangle. Angles are specified as degrees, sides in units of points (1/72 in.).
358
494
  * @param {Object} [options] - The options
359
- * @param {string} [options.traitID='sss'] - indicates what type of data is being passed in the traits parameter.
495
+ * @param {Recipe.TriangleTrait} [options.traitID='sss'] - indicates what type of data is being passed in the traits parameter,
496
+ * one of the `Recipe.TriangleTrait` values:
360
497
  * ('sss'- three side lengths, 'sas' - side-angle-side (sideA, <C, sideB), 'asa' - angle-side-angle (<B, sideC, <A),
361
498
  * or 'vtx' - three vertex points [x,y])
362
- * @param {string} [options.position='b'] - the position of the triangle to be set at the given x,y coordinates.
499
+ * @param {Recipe.TrianglePosition} [options.position='b'] - the position of the triangle to be set at the given x,y coordinates,
500
+ * one of the `Recipe.TrianglePosition` values.
363
501
  * The values can be one of: 'A' - the A vertex (right vertex of triangle base), 'B' - the B vertex (left vertex of triangle base),
364
502
  * 'C' - the C vertex (apex of triangle), 'centroid', 'circumcenter', or 'incenter' of the triangle.
365
503
  * @param {Boolean} [options.flipX=false] - flip triangle up to down through rotation point.
@@ -375,12 +513,15 @@ function flipY(x, ngon) {
375
513
  * @param {number} [options.skewX] - the angle skew off the x-axis
376
514
  * @param {number} [options.skewY] - the angle skew off the y-axis.
377
515
  * @returns {Recipe} The recipe instance.
516
+ * @param {string} [options.link] - Make the triangle's bounding box open this URL.
517
+ * @param {boolean} [options.debug] - Also draw the reference points and labels.
378
518
  * @throws {Error} If traits does not contain three values or does not define a valid triangle.
519
+ * @throws {TypeError} If no page is active.
379
520
  */
380
521
 
381
522
  exports.triangle = function triangle(x, y, traits, options = {}) {
382
- let traitID = options.traitID || options.traitsID || "sss";
383
- let position = options.position ? options.position.toLowerCase() : "default";
523
+ let traitID = options.traitID || options.traitsID || TriangleTrait.SSS;
524
+ let position = options.position ? options.position.toLowerCase() : null;
384
525
  let triopts = Object.assign({}, options);
385
526
  delete triopts.link;
386
527
 
@@ -398,25 +539,25 @@ exports.triangle = function triangle(x, y, traits, options = {}) {
398
539
  let cc;
399
540
  let ic;
400
541
  switch (position) {
401
- case "centroid":
542
+ case TrianglePosition.CENTROID:
402
543
  pt = triangle.centroid;
403
544
  break;
404
- case "circumcenter":
545
+ case TrianglePosition.CIRCUMCENTER:
405
546
  cc = triangle.circumcenter;
406
547
  [pt, radius] = [cc.point, cc.radius];
407
548
  break;
408
- case "incenter":
549
+ case TrianglePosition.INCENTER:
409
550
  ic = triangle.incenter;
410
551
  [pt, radius] = [ic.point, ic.radius];
411
552
  triangle.incenter = [x, y]; // have to update incenter because tranlation will change it.
412
553
  break;
413
- case "a":
554
+ case TrianglePosition.A:
414
555
  pt = new Point(triangle.A);
415
556
  break;
416
- case "b":
557
+ case TrianglePosition.B:
417
558
  pt = new Point(triangle.B);
418
559
  break;
419
- case "c":
560
+ case TrianglePosition.C:
420
561
  pt = new Point(triangle.C);
421
562
  break;
422
563
  default:
@@ -468,13 +609,13 @@ exports.triangle = function triangle(x, y, traits, options = {}) {
468
609
  for (const vertex of trigon) {
469
610
  tgon.push(rotate(vertex[0], vertex[1], rx, ry, angle));
470
611
  }
471
- triangle = new Triangle(tgon[0][0], tgon[0][1], "vtx", tgon);
612
+ triangle = new Triangle(tgon[0][0], tgon[0][1], TriangleTrait.VTX, tgon);
472
613
  }
473
614
  this.circle(x, y, 2, { color: "red", width: 0.5 });
474
615
 
475
616
  if (radius) {
476
617
  this.circle(x, y, radius, { color: "green", width: 0.5 });
477
- } else if (position === "centroid") {
618
+ } else if (position === TrianglePosition.CENTROID) {
478
619
  const ma_A = new Line(triangle.A, triangle.BC.midpoint);
479
620
  const mb_B = new Line(triangle.B, triangle.AC.midpoint);
480
621
  const mc_C = new Line(triangle.C, triangle.AB.midpoint);
@@ -549,12 +690,24 @@ exports.triangle = function triangle(x, y, traits, options = {}) {
549
690
  // /_____\
550
691
  // B c A
551
692
 
693
+ /**
694
+ * A triangle solved from side lengths, angles or vertices, with vertex B at
695
+ * (x, y) and side c along the x axis unless vertices are given.
696
+ * @private
697
+ */
552
698
  const Triangle = class Triangle {
699
+ /**
700
+ * @param {number} x - The x of vertex B.
701
+ * @param {number} y - The y of vertex B.
702
+ * @param {Recipe.TriangleTrait} traitID - How `traits` define the triangle, case-insensitive.
703
+ * @param {Array} traits - Three side lengths and/or angles in degrees, or three [x, y] vertices.
704
+ * @throws {Error} If the traits do not define a valid triangle or the trait is unknown.
705
+ */
553
706
  constructor(x, y, traitID, traits) {
554
707
  let a, b, c, angA, angB, angC;
555
708
  let sss, BC, AC, AB;
556
709
  switch (traitID.toLowerCase()) {
557
- case "sss":
710
+ case TriangleTrait.SSS:
558
711
  sss = traits.slice().sort((a, b) => {
559
712
  return a - b;
560
713
  });
@@ -567,12 +720,12 @@ const Triangle = class Triangle {
567
720
  [a, b, c] = traits;
568
721
  break;
569
722
 
570
- case "sas":
723
+ case TriangleTrait.SAS:
571
724
  [a, angC, b] = traits;
572
725
  c = Math.sqrt(a * a + b * b - 2 * a * b * Math.cos(toRadians(angC)));
573
726
  break;
574
727
 
575
- case "asa":
728
+ case TriangleTrait.ASA:
576
729
  [angB, c, angA] = traits;
577
730
  angC = 180 - angA - angB;
578
731
  if (angC <= 0) {
@@ -584,7 +737,7 @@ const Triangle = class Triangle {
584
737
  b = (c * Math.sin(toRadians(angB))) / Math.sin(toRadians(angC));
585
738
  break;
586
739
 
587
- case "vtx":
740
+ case TriangleTrait.VTX:
588
741
  this._B = traits[0];
589
742
  this._C = traits[1];
590
743
  this._A = traits[2];
@@ -627,16 +780,32 @@ const Triangle = class Triangle {
627
780
  }
628
781
  }
629
782
 
783
+ /**
784
+ * Vertex A, the right end of the base.
785
+ * @returns {number[]} The [x, y] point.
786
+ */
630
787
  get A() {
631
788
  return this._A;
632
789
  }
790
+ /**
791
+ * Vertex B, the left end of the base.
792
+ * @returns {number[]} The [x, y] point.
793
+ */
633
794
  get B() {
634
795
  return this._B;
635
796
  }
797
+ /**
798
+ * Vertex C, the apex.
799
+ * @returns {number[]} The [x, y] point.
800
+ */
636
801
  get C() {
637
802
  return this._C;
638
803
  }
639
804
 
805
+ /**
806
+ * Side b, from A to C.
807
+ * @returns {Line} The side.
808
+ */
640
809
  get AC() {
641
810
  if (!this._AC) {
642
811
  this._AC = new Line(this._A, this._C);
@@ -644,6 +813,10 @@ const Triangle = class Triangle {
644
813
  return this._AC;
645
814
  }
646
815
 
816
+ /**
817
+ * Side c, from A to B.
818
+ * @returns {Line} The side.
819
+ */
647
820
  get AB() {
648
821
  if (!this._AB) {
649
822
  this._AB = new Line(this._A, this._B);
@@ -651,6 +824,10 @@ const Triangle = class Triangle {
651
824
  return this._AB;
652
825
  }
653
826
 
827
+ /**
828
+ * Side a, from B to C.
829
+ * @returns {Line} The side.
830
+ */
654
831
  get BC() {
655
832
  if (!this._BC) {
656
833
  this._BC = new Line(this._B, this._C);
@@ -658,10 +835,18 @@ const Triangle = class Triangle {
658
835
  return this._BC;
659
836
  }
660
837
 
838
+ /**
839
+ * The sum of the side lengths.
840
+ * @returns {number} The perimeter.
841
+ */
661
842
  get perimeter() {
662
843
  return this._perimeter;
663
844
  }
664
845
 
846
+ /**
847
+ * The area, by Heron's formula.
848
+ * @returns {number} The area.
849
+ */
665
850
  get area() {
666
851
  if (!this._area) {
667
852
  // Heron's formula
@@ -672,6 +857,10 @@ const Triangle = class Triangle {
672
857
  return this._area;
673
858
  }
674
859
 
860
+ /**
861
+ * The vertices in drawing order B, C, A.
862
+ * @returns {number[][]} The [x, y] points.
863
+ */
675
864
  get vertices() {
676
865
  return [this._B, this._C, this._A];
677
866
  }
@@ -679,6 +868,10 @@ const Triangle = class Triangle {
679
868
  // The centroid is the point where all three medians of the triangle
680
869
  // intersect. A median is the line running from a vertex to the midpoint
681
870
  // of the side opposite the vertex.
871
+ /**
872
+ * The intersection of the medians.
873
+ * @returns {Point} The centroid.
874
+ */
682
875
  get centroid() {
683
876
  if (!this._centroid) {
684
877
  let AB = new Line(this._A, this._B);
@@ -694,6 +887,10 @@ const Triangle = class Triangle {
694
887
 
695
888
  // The intersection of the perpendicular bisectors of
696
889
  // each side midpoint defines the circumcenter.
890
+ /**
891
+ * The center and radius of the circumscribed circle.
892
+ * @returns {{point: Point, radius: number}} The circumcircle.
893
+ */
697
894
  get circumcenter() {
698
895
  if (!this._circumcenter) {
699
896
  // Algorithm in use is defining a circle from three noncolinear planar points
@@ -718,6 +915,10 @@ const Triangle = class Triangle {
718
915
  return this._circumcenter;
719
916
  }
720
917
 
918
+ /**
919
+ * Move the incenter point, keeping its radius.
920
+ * @param {number[]} center - The new [x, y] point.
921
+ */
721
922
  set incenter(center) {
722
923
  this._incenter = {
723
924
  point: new Point(center[0], center[1]),
@@ -725,6 +926,10 @@ const Triangle = class Triangle {
725
926
  };
726
927
  }
727
928
 
929
+ /**
930
+ * The center and radius of the inscribed circle.
931
+ * @returns {{point: Point, radius: number}} The incircle.
932
+ */
728
933
  get incenter() {
729
934
  if (!this._incenter) {
730
935
  // https://www.mathopenref.com/coordincenter.html
@@ -774,21 +979,31 @@ const Triangle = class Triangle {
774
979
  * @param {number} x x-coordinate position
775
980
  * @param {number} y y-coordinate position
776
981
  * @param {Object} [options] arrow and polygon options
777
- * @param {number} [options.type=0] indicates the type of arrow head to produce. (0-'triangle', 1-'dart', 2-'kite')
778
- * Number or name may be used. Note, that the value of base offset in head option overrides this value.
982
+ * @param {Recipe.ArrowType|number} [options.type=0] indicates the type of arrow head to produce,
983
+ * a `Recipe.ArrowType` value or its number (0-'triangle', 1-'dart', 2-'kite').
984
+ * Note, that the value of base offset in head option overrides this value.
779
985
  * @param {number|number[]} [options.head=[10,20,0]] defines the length, width and base offset of arrow head.
780
986
  * A single number can be used to assign both the length and width of arrow, giving the base offset value as zero.
781
987
  * @param {number|number[]} [options.shaft=[10,10]] defines the length and width of the arrow shaft.
782
988
  * @param {Boolean} [options.double=false] indicate double headed arrow production.
783
- * @param {string} [options.at] position and/or rotate at "head" or "tail" of arrow instead of at center.
989
+ * @param {Recipe.ArrowAt} [options.at] position and/or rotate at the `Recipe.ArrowAt` head or tail of arrow instead of at center.
990
+ * @param {number|boolean} [options.debug] Draw the drop point; 2 also labels the reference points.
784
991
  * @returns {Recipe} The recipe instance.
992
+ * @throws {TypeError} If no page is active.
785
993
  */
786
994
  exports.arrow = function arrow(x, y, options = {}) {
787
995
  let defaultHeadLength = 10;
788
996
  let nock = null;
789
997
  let ox = x;
790
998
  let debug = options.debug;
791
- let headTypes = { 0: 0, triangle: 0, 1: 0.5, dart: 0.5, 2: -1, kite: -1 };
999
+ let headTypes = {
1000
+ 0: 0,
1001
+ [ArrowType.TRIANGLE]: 0,
1002
+ 1: 0.5,
1003
+ [ArrowType.DART]: 0.5,
1004
+ 2: -1,
1005
+ [ArrowType.KITE]: -1,
1006
+ };
792
1007
 
793
1008
  let shaftLength = defaultHeadLength;
794
1009
  let shaftWidth = defaultHeadLength;
@@ -852,10 +1067,10 @@ exports.arrow = function arrow(x, y, options = {}) {
852
1067
  // ('default' choice represents center of arrow and default rotation point)
853
1068
  if (options.double) {
854
1069
  switch (options.at) {
855
- case "head":
1070
+ case ArrowAt.HEAD:
856
1071
  x -= headLength;
857
1072
  break;
858
- case "tail":
1073
+ case ArrowAt.TAIL:
859
1074
  x += shaftLength + headLength;
860
1075
  break;
861
1076
  default:
@@ -864,10 +1079,10 @@ exports.arrow = function arrow(x, y, options = {}) {
864
1079
  nock = new Kite(x - shaftLength, y, headLength, headWidth, baseOffset);
865
1080
  } else {
866
1081
  switch (options.at) {
867
- case "head":
1082
+ case ArrowAt.HEAD:
868
1083
  x -= headLength;
869
1084
  break;
870
- case "tail":
1085
+ case ArrowAt.TAIL:
871
1086
  x += shaftLength;
872
1087
  break;
873
1088
  default:
@@ -910,15 +1125,15 @@ exports.arrow = function arrow(x, y, options = {}) {
910
1125
  [
911
1126
  arrow.tip.I, // tip point of arrow
912
1127
  arrow.tip.T,
913
- arrow.shaft("br"), // lower connection point to arrow tip
914
- arrow.shaft("bl"),
1128
+ arrow.shaft(ShaftCorner.BOTTOM_RIGHT), // lower connection point to arrow tip
1129
+ arrow.shaft(ShaftCorner.BOTTOM_LEFT),
915
1130
 
916
1131
  arrow.nock.Tp, // drawing reverse arrow head at nock/tail of arrow
917
1132
  arrow.nock.Ip,
918
1133
  arrow.nock.Kp,
919
1134
 
920
- arrow.shaft("tl"),
921
- arrow.shaft("tr"), // upper connection point to arrow tip
1135
+ arrow.shaft(ShaftCorner.TOP_LEFT),
1136
+ arrow.shaft(ShaftCorner.TOP_RIGHT), // upper connection point to arrow tip
922
1137
  arrow.tip.K,
923
1138
  arrow.tip.I,
924
1139
  ],
@@ -942,10 +1157,10 @@ exports.arrow = function arrow(x, y, options = {}) {
942
1157
  [
943
1158
  arrow.tip.I, // tip point of arrow
944
1159
  arrow.tip.T,
945
- arrow.shaft("br"), // lower connection point to arrow tip
946
- arrow.shaft("bl"),
947
- arrow.shaft("tl"),
948
- arrow.shaft("tr"), // upper connection point to arrow tip
1160
+ arrow.shaft(ShaftCorner.BOTTOM_RIGHT), // lower connection point to arrow tip
1161
+ arrow.shaft(ShaftCorner.BOTTOM_LEFT),
1162
+ arrow.shaft(ShaftCorner.TOP_LEFT),
1163
+ arrow.shaft(ShaftCorner.TOP_RIGHT), // upper connection point to arrow tip
949
1164
  arrow.tip.K,
950
1165
  arrow.tip.I,
951
1166
  ],
@@ -994,16 +1209,16 @@ exports.arrow = function arrow(x, y, options = {}) {
994
1209
  color: kcc,
995
1210
  width: 0.5,
996
1211
  });
997
- let br = arrow.shaft("br");
1212
+ let br = arrow.shaft(ShaftCorner.BOTTOM_RIGHT);
998
1213
  this.text("br", br[0] - 4, br[1] - 11, { size: 9, color: tc });
999
1214
  this.circle(br[0], br[1] - 8, 6, { color: cc, width: 0.5 });
1000
- let bl = arrow.shaft("bl");
1215
+ let bl = arrow.shaft(ShaftCorner.BOTTOM_LEFT);
1001
1216
  this.text("bl", bl[0] + 4, bl[1] - 11, { size: 9, color: tc });
1002
1217
  this.circle(bl[0] + 8, bl[1] - 8, 6, { color: cc, width: 0.5 });
1003
- let tl = arrow.shaft("tl");
1218
+ let tl = arrow.shaft(ShaftCorner.TOP_LEFT);
1004
1219
  this.text("tl", tl[0] + 5, tl[1] + 2, { size: 9, color: tc });
1005
1220
  this.circle(tl[0] + 8, tl[1] + 7, 6, { color: cc, width: 0.5 });
1006
- let tr = arrow.shaft("tr");
1221
+ let tr = arrow.shaft(ShaftCorner.TOP_RIGHT);
1007
1222
  this.text("tr", tr[0] - 3, tr[1] + 2, { size: 9, color: tc });
1008
1223
  this.circle(tr[0], tr[1] + 7, 6, { color: cc, width: 0.5 });
1009
1224
  }
@@ -1030,7 +1245,19 @@ exports.arrow = function arrow(x, y, options = {}) {
1030
1245
  // |________________________|_______________|
1031
1246
  // base offset height
1032
1247
 
1248
+ /**
1249
+ * The quadrilateral arrow head: a triangle, dart or kite depending on the
1250
+ * base offset.
1251
+ * @private
1252
+ */
1033
1253
  const Kite = class Kite {
1254
+ /**
1255
+ * @param {number} x - The x of the head base.
1256
+ * @param {number} y - The y of the arrow axis.
1257
+ * @param {number} width - The head length along the axis.
1258
+ * @param {number} height - The head width across the axis.
1259
+ * @param {number} [baseOffset=0] - Positive for a dart, negative for a kite.
1260
+ */
1034
1261
  constructor(x, y, width, height, baseOffset = 0) {
1035
1262
  this._x = x;
1036
1263
  this._y = y;
@@ -1041,7 +1268,11 @@ const Kite = class Kite {
1041
1268
  // but it cannot exceed the height of the arrow head.
1042
1269
  this._baseOffset = baseOffset >= height ? height - 1 : baseOffset;
1043
1270
  this._type =
1044
- baseOffset > 0 ? "dart" : baseOffset === 0 ? "triangle" : "kite";
1271
+ baseOffset > 0
1272
+ ? ArrowType.DART
1273
+ : baseOffset === 0
1274
+ ? ArrowType.TRIANGLE
1275
+ : ArrowType.KITE;
1045
1276
 
1046
1277
  this._K = new Point(x, y - height / 2);
1047
1278
  this._I = new Point(x + width, y);
@@ -1049,34 +1280,70 @@ const Kite = class Kite {
1049
1280
  this._E = new Point(x + this._baseOffset, y);
1050
1281
  }
1051
1282
 
1283
+ /**
1284
+ * Point K, the head corner above the axis.
1285
+ * @returns {number[]} The [x, y] point.
1286
+ */
1052
1287
  get K() {
1053
1288
  return [this._K.x, this._K.y];
1054
1289
  }
1290
+ /**
1291
+ * Point I, the head tip.
1292
+ * @returns {number[]} The [x, y] point.
1293
+ */
1055
1294
  get I() {
1056
1295
  return [this._I.x, this._I.y];
1057
1296
  }
1297
+ /**
1298
+ * Point T, the head corner below the axis.
1299
+ * @returns {number[]} The [x, y] point.
1300
+ */
1058
1301
  get T() {
1059
1302
  return [this._T.x, this._T.y];
1060
1303
  }
1304
+ /**
1305
+ * Point E, where the head base meets the axis.
1306
+ * @returns {number[]} The [x, y] point.
1307
+ */
1061
1308
  get E() {
1062
1309
  return [this._E.x, this._E.y];
1063
1310
  }
1064
1311
 
1065
1312
  // create points I&E prime (flip, 180 degrees) to change direction of Kite on X-axis
1313
+ /**
1314
+ * Point I mirrored to point the head the other way.
1315
+ * @returns {number[]} The [x, y] point.
1316
+ */
1066
1317
  get Ip() {
1067
1318
  return [this._I.x - 2 * this._width, this._I.y];
1068
1319
  }
1320
+ /**
1321
+ * Point E mirrored to point the head the other way.
1322
+ * @returns {number[]} The [x, y] point.
1323
+ */
1069
1324
  get Ep() {
1070
1325
  return [this._E.x + 2 * this._baseOffset, this._E.y];
1071
1326
  }
1072
1327
 
1328
+ /**
1329
+ * Point K for a mirrored head; the same as K.
1330
+ * @returns {number[]} The [x, y] point.
1331
+ */
1073
1332
  get Kp() {
1074
1333
  return [this._K.x, this._K.y];
1075
1334
  } // no different than K or T, just here for consistency usage
1335
+ /**
1336
+ * Point T for a mirrored head; the same as T.
1337
+ * @returns {number[]} The [x, y] point.
1338
+ */
1076
1339
  get Tp() {
1077
1340
  return [this._T.x, this._T.y];
1078
1341
  }
1079
1342
 
1343
+ /**
1344
+ * The line from K to E.
1345
+ * @returns {Line} The segment.
1346
+ */
1080
1347
  get KE() {
1081
1348
  // line segment between points K and E
1082
1349
  if (!this._KE) {
@@ -1085,6 +1352,10 @@ const Kite = class Kite {
1085
1352
  return this._KE;
1086
1353
  }
1087
1354
 
1355
+ /**
1356
+ * The line from T to E.
1357
+ * @returns {Line} The segment.
1358
+ */
1088
1359
  get TE() {
1089
1360
  // line segment between points T and E
1090
1361
  if (!this._TE) {
@@ -1093,6 +1364,10 @@ const Kite = class Kite {
1093
1364
  return this._TE;
1094
1365
  }
1095
1366
 
1367
+ /**
1368
+ * The head shape.
1369
+ * @returns {Recipe.ArrowType} The `Recipe.ArrowType` value.
1370
+ */
1096
1371
  get type() {
1097
1372
  return this._type;
1098
1373
  }
@@ -1102,7 +1377,19 @@ const Kite = class Kite {
1102
1377
  // }
1103
1378
  };
1104
1379
 
1380
+ /**
1381
+ * An arrow: a head, an optional reverse head at the tail, and a shaft.
1382
+ * @private
1383
+ */
1105
1384
  const Arrow = class Arrow {
1385
+ /**
1386
+ * @param {number} x - The x where the shaft meets the head.
1387
+ * @param {number} y - The y of the arrow axis.
1388
+ * @param {Kite} arrowhead - The head.
1389
+ * @param {number} shaftLength - The shaft length.
1390
+ * @param {number} shaftWidth - The shaft width.
1391
+ * @param {Kite|null} nock - The reverse head of a double arrow.
1392
+ */
1106
1393
  constructor(x, y, arrowhead, shaftLength, shaftWidth, nock) {
1107
1394
  this._x = x;
1108
1395
  this._y = y;
@@ -1114,33 +1401,59 @@ const Arrow = class Arrow {
1114
1401
  this._connectAt_br = new Point(this._x, this._y + shaftWidth / 2); // bottom, right
1115
1402
  }
1116
1403
 
1404
+ /**
1405
+ * The arrow head.
1406
+ * @returns {Kite} The head.
1407
+ */
1117
1408
  get tip() {
1118
1409
  return this._tip;
1119
1410
  }
1411
+ /**
1412
+ * The reverse head of a double arrow.
1413
+ * @returns {Kite|null} The reverse head, or null.
1414
+ */
1120
1415
  get nock() {
1121
1416
  return this._nock;
1122
1417
  }
1123
1418
 
1419
+ /**
1420
+ * Move the shaft end so it meets a dart or kite head.
1421
+ * @param {Point} pointTR - Where the top edge of the shaft meets the head.
1422
+ * @returns {void}
1423
+ */
1124
1424
  joinShaft(pointTR) {
1125
1425
  this._connectAt_tr.x = pointTR.x;
1126
1426
  this._connectAt_br.x = pointTR.x;
1127
1427
  }
1128
1428
 
1429
+ /**
1430
+ * A corner of the shaft.
1431
+ * @param {string} point - A `ShaftCorner` value.
1432
+ * @returns {number[]|undefined} The [x, y] corner, or undefined for an unknown corner.
1433
+ */
1129
1434
  shaft(point) {
1130
1435
  switch (point) {
1131
- case "br":
1436
+ case ShaftCorner.BOTTOM_RIGHT:
1132
1437
  return [this._connectAt_br.x, this._connectAt_br.y];
1133
- case "bl":
1438
+ case ShaftCorner.BOTTOM_LEFT:
1134
1439
  return [this._x - this._shaftLength, this._connectAt_br.y];
1135
- case "tl":
1440
+ case ShaftCorner.TOP_LEFT:
1136
1441
  return [this._x - this._shaftLength, this._connectAt_tr.y];
1137
- case "tr":
1442
+ case ShaftCorner.TOP_RIGHT:
1138
1443
  return [this._connectAt_tr.x, this._connectAt_tr.y];
1139
1444
  }
1140
1445
  }
1141
1446
  };
1142
1447
 
1448
+ /**
1449
+ * A mutable 2D point.
1450
+ * @private
1451
+ */
1143
1452
  const Point = class Point {
1453
+ /**
1454
+ * @param {number|number[]} x - The x, or an [x, y] array.
1455
+ * @param {number} [y] - The y when x is a number.
1456
+ */
1144
1457
  constructor(x, y) {
1145
1458
  if (Array.isArray(x)) {
1146
1459
  this._x = x[0];
@@ -1150,27 +1463,56 @@ const Point = class Point {
1150
1463
  this._y = y;
1151
1464
  }
1152
1465
  }
1466
+ /**
1467
+ * @returns {number} The x coordinate.
1468
+ */
1153
1469
  get x() {
1154
1470
  return this._x;
1155
1471
  }
1472
+ /**
1473
+ * @returns {number} The y coordinate.
1474
+ */
1156
1475
  get y() {
1157
1476
  return this._y;
1158
1477
  }
1478
+ /**
1479
+ * @returns {number[]} The [x, y] array.
1480
+ */
1159
1481
  get point() {
1160
1482
  return [this._x, this._y];
1161
1483
  }
1484
+ /**
1485
+ * @param {number} xx - The new x coordinate.
1486
+ */
1162
1487
  set x(xx) {
1163
1488
  this._x = xx;
1164
1489
  }
1490
+ /**
1491
+ * @param {number} yy - The new y coordinate.
1492
+ */
1165
1493
  set y(yy) {
1166
1494
  this._y = yy;
1167
1495
  }
1496
+ /**
1497
+ * @param {number[]} pnt - The new [x, y] array.
1498
+ */
1168
1499
  set point(pnt) {
1169
1500
  [this._x, this._y] = pnt;
1170
1501
  }
1171
1502
  };
1172
1503
 
1504
+ /**
1505
+ * A line segment between two points.
1506
+ * @private
1507
+ */
1173
1508
  const Line = class Line {
1509
+ /**
1510
+ * @param {number|number[]|Point} x1 - The first x, or the first point.
1511
+ * @param {number|number[]|Point} y1 - The first y, or the second point.
1512
+ * @param {number} [x2] - The second x when numbers are given.
1513
+ * @param {number} [y2] - The second y when numbers are given.
1514
+ * @throws {Error} If the first argument is a point and the second is not.
1515
+ */
1174
1516
  constructor(x1, y1, x2, y2) {
1175
1517
  // Allow user to supply Points or Arrays instead of individual coordinates
1176
1518
  if (
@@ -1199,24 +1541,38 @@ const Line = class Line {
1199
1541
  }
1200
1542
  }
1201
1543
 
1544
+ /**
1545
+ * @returns {boolean} True when both points share the same x.
1546
+ */
1202
1547
  get isVertical() {
1203
1548
  return this._pt1.x === this._pt2.x;
1204
1549
  }
1205
1550
 
1551
+ /**
1552
+ * One end point.
1553
+ * @param {number} ep - 1 for the first point, 2 for the second.
1554
+ * @returns {Point|null} The point, or null for another value.
1555
+ */
1206
1556
  point(ep) {
1207
1557
  return ep === 1 ? this._pt1 : ep === 2 ? this._pt2 : null;
1208
1558
  }
1209
1559
 
1560
+ /**
1561
+ * @returns {Point} The midpoint of the segment.
1562
+ */
1210
1563
  get midpoint() {
1211
1564
  if (!this._midpoint) {
1212
1565
  let dx = (this._pt2.x - this._pt1.x) / 2;
1213
1566
  let dy = (this._pt2.y - this._pt1.y) / 2;
1214
- this._midPoint = new Point(this._pt1.x + dx, this._pt1.y + dy);
1567
+ this._midpoint = new Point(this._pt1.x + dx, this._pt1.y + dy);
1215
1568
  }
1216
1569
 
1217
- return this._midPoint;
1570
+ return this._midpoint;
1218
1571
  }
1219
1572
 
1573
+ /**
1574
+ * @returns {number} The segment length.
1575
+ */
1220
1576
  get length() {
1221
1577
  if (!this._length) {
1222
1578
  this._length = Math.sqrt(
@@ -1228,6 +1584,9 @@ const Line = class Line {
1228
1584
  return this._length;
1229
1585
  }
1230
1586
 
1587
+ /**
1588
+ * @returns {number} The slope; Infinity for a vertical line.
1589
+ */
1231
1590
  get slope() {
1232
1591
  if (!this._slope) {
1233
1592
  this._slope =
@@ -1239,11 +1598,20 @@ const Line = class Line {
1239
1598
  return this._slope;
1240
1599
  }
1241
1600
 
1601
+ /**
1602
+ * @returns {number} The negative inverse slope, of a perpendicular line.
1603
+ */
1242
1604
  get inv_slope() {
1243
1605
  // inverse slope
1244
1606
  return -(1 / this.slope);
1245
1607
  }
1246
1608
 
1609
+ /**
1610
+ * The point past an end of the segment, continuing along the line.
1611
+ * @param {number} distance - How far past the end.
1612
+ * @param {number} [ptNbr=2] - The end to extend: 1 or 2.
1613
+ * @returns {Point} The extended point.
1614
+ */
1247
1615
  extend(distance, ptNbr = 2) {
1248
1616
  let slope = this.slope;
1249
1617
  let ept = this.point(ptNbr);
@@ -1298,6 +1666,11 @@ const Line = class Line {
1298
1666
  // y = ----------------------------------------------- ---------------
1299
1667
  // (x2 - x1)(y4 - y3) - (x4 - x3)(y2 - y1) b1(a2) - b2(a1)
1300
1668
 
1669
+ /**
1670
+ * The intersection with another line.
1671
+ * @param {Line} CD - The other line.
1672
+ * @returns {Point|null} The intersection, or null for parallel lines.
1673
+ */
1301
1674
  intersect(CD) {
1302
1675
  let A = this._pt1,
1303
1676
  B = this._pt2;