@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.
- package/README.md +57 -7
- package/THIRD_PARTY_NOTICES.md +39 -0
- package/index.js +120 -1
- package/lib/PDFRStreamForBuffer.js +52 -7
- package/lib/PDFRStreamForFile.js +56 -7
- package/lib/PDFStreamForResponse.js +22 -5
- package/lib/PDFWStreamForBuffer.js +49 -10
- package/lib/PDFWStreamForFile.js +26 -5
- package/lib/Recipe.js +86 -30
- package/lib/content-stream.js +503 -0
- package/lib/css-colors.js +149 -0
- package/lib/font-text.js +1461 -0
- package/lib/glyph-list.js +1127 -0
- package/lib/recipe/annotation.js +482 -105
- package/lib/recipe/appendPage.js +8 -1
- package/lib/recipe/colors.js +132 -35
- package/lib/recipe/coordinate.js +82 -16
- package/lib/recipe/encrypt.js +32 -13
- package/lib/recipe/font.js +70 -25
- package/lib/recipe/graphics-state.js +3 -2
- package/lib/recipe/htmlToTextObjects.js +154 -21
- package/lib/recipe/image.js +34 -9
- package/lib/recipe/info.js +93 -22
- package/lib/recipe/insertPage.js +12 -3
- package/lib/recipe/overlay.js +4 -2
- package/lib/recipe/page-references.js +436 -0
- package/lib/recipe/page.js +379 -275
- package/lib/recipe/parameters.js +11 -2
- package/lib/recipe/replaceText.js +464 -30
- package/lib/recipe/shapes.js +418 -45
- package/lib/recipe/split.js +6 -2
- package/lib/recipe/table.js +260 -173
- package/lib/recipe/text.helper.js +176 -10
- package/lib/recipe/text.js +420 -115
- package/lib/recipe/utils.js +66 -3
- package/lib/recipe/vector-line.js +101 -39
- package/lib/recipe/vector-polygon.js +18 -6
- package/lib/recipe/vector.helper.js +150 -13
- package/lib/recipe/vector.js +150 -53
- package/lib/recipe/xObjectForm.js +73 -10
- package/lib/recipe-constants.js +373 -0
- package/muhammara.d.ts +3487 -152
- package/package.json +1 -1
package/lib/recipe/shapes.js
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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 ||
|
|
383
|
-
let position = options.position ? options.position.toLowerCase() :
|
|
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
|
|
542
|
+
case TrianglePosition.CENTROID:
|
|
402
543
|
pt = triangle.centroid;
|
|
403
544
|
break;
|
|
404
|
-
case
|
|
545
|
+
case TrianglePosition.CIRCUMCENTER:
|
|
405
546
|
cc = triangle.circumcenter;
|
|
406
547
|
[pt, radius] = [cc.point, cc.radius];
|
|
407
548
|
break;
|
|
408
|
-
case
|
|
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
|
|
554
|
+
case TrianglePosition.A:
|
|
414
555
|
pt = new Point(triangle.A);
|
|
415
556
|
break;
|
|
416
|
-
case
|
|
557
|
+
case TrianglePosition.B:
|
|
417
558
|
pt = new Point(triangle.B);
|
|
418
559
|
break;
|
|
419
|
-
case
|
|
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],
|
|
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 ===
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
778
|
-
*
|
|
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 {
|
|
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 = {
|
|
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
|
|
1070
|
+
case ArrowAt.HEAD:
|
|
856
1071
|
x -= headLength;
|
|
857
1072
|
break;
|
|
858
|
-
case
|
|
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
|
|
1082
|
+
case ArrowAt.HEAD:
|
|
868
1083
|
x -= headLength;
|
|
869
1084
|
break;
|
|
870
|
-
case
|
|
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(
|
|
914
|
-
arrow.shaft(
|
|
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(
|
|
921
|
-
arrow.shaft(
|
|
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(
|
|
946
|
-
arrow.shaft(
|
|
947
|
-
arrow.shaft(
|
|
948
|
-
arrow.shaft(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
1436
|
+
case ShaftCorner.BOTTOM_RIGHT:
|
|
1132
1437
|
return [this._connectAt_br.x, this._connectAt_br.y];
|
|
1133
|
-
case
|
|
1438
|
+
case ShaftCorner.BOTTOM_LEFT:
|
|
1134
1439
|
return [this._x - this._shaftLength, this._connectAt_br.y];
|
|
1135
|
-
case
|
|
1440
|
+
case ShaftCorner.TOP_LEFT:
|
|
1136
1441
|
return [this._x - this._shaftLength, this._connectAt_tr.y];
|
|
1137
|
-
case
|
|
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.
|
|
1567
|
+
this._midpoint = new Point(this._pt1.x + dx, this._pt1.y + dy);
|
|
1215
1568
|
}
|
|
1216
1569
|
|
|
1217
|
-
return this.
|
|
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;
|