geometric-library 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/cjs/abstracts/Angle.js +60 -0
- package/dist/cjs/abstracts/Figure.js +147 -0
- package/dist/cjs/abstracts/Flag.js +27 -0
- package/dist/cjs/abstracts/Magnitude.js +25 -0
- package/dist/cjs/abstracts/Point.js +50 -0
- package/dist/cjs/abstracts/Vector.js +74 -0
- package/dist/cjs/figures/ArcCurve.js +132 -0
- package/dist/cjs/figures/Circle.js +43 -0
- package/dist/cjs/figures/CubicBezierCurve.js +149 -0
- package/dist/cjs/figures/Ellipse.js +111 -0
- package/dist/cjs/figures/Line.js +264 -0
- package/dist/cjs/figures/Polygon.js +17 -0
- package/dist/cjs/figures/QuadraticBezierCurve.js +104 -0
- package/dist/cjs/index.js +48 -0
- package/dist/cjs/types/index.js +2 -0
- package/dist/cjs/utilities/Calculator.js +153 -0
- package/dist/cjs/utilities/index.js +10 -0
- package/dist/esm/abstracts/Angle.js +56 -0
- package/dist/esm/abstracts/Figure.js +143 -0
- package/dist/esm/abstracts/Flag.js +23 -0
- package/dist/esm/abstracts/Magnitude.js +21 -0
- package/dist/esm/abstracts/Point.js +46 -0
- package/dist/esm/abstracts/Vector.js +70 -0
- package/dist/esm/figures/ArcCurve.js +128 -0
- package/dist/esm/figures/Circle.js +39 -0
- package/dist/esm/figures/CubicBezierCurve.js +145 -0
- package/dist/esm/figures/Ellipse.js +107 -0
- package/dist/esm/figures/Line.js +260 -0
- package/dist/esm/figures/Polygon.js +13 -0
- package/dist/esm/figures/QuadraticBezierCurve.js +100 -0
- package/dist/esm/index.js +17 -0
- package/dist/esm/types/index.js +1 -0
- package/dist/esm/utilities/Calculator.js +149 -0
- package/dist/esm/utilities/index.js +7 -0
- package/dist/types/abstracts/Angle.d.ts +18 -0
- package/dist/types/abstracts/Figure.d.ts +22 -0
- package/dist/types/abstracts/Flag.d.ts +10 -0
- package/dist/types/abstracts/Magnitude.d.ts +9 -0
- package/dist/types/abstracts/Point.d.ts +14 -0
- package/dist/types/abstracts/Vector.d.ts +20 -0
- package/dist/types/figures/ArcCurve.d.ts +23 -0
- package/dist/types/figures/Circle.d.ts +12 -0
- package/dist/types/figures/CubicBezierCurve.d.ts +23 -0
- package/dist/types/figures/Ellipse.d.ts +26 -0
- package/dist/types/figures/Line.d.ts +44 -0
- package/dist/types/figures/Polygon.d.ts +7 -0
- package/dist/types/figures/QuadraticBezierCurve.d.ts +21 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/types/index.d.ts +144 -0
- package/dist/types/utilities/Calculator.d.ts +49 -0
- package/dist/types/utilities/index.d.ts +6 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Dante Darie
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# geometric-library
|
|
2
|
+
|
|
3
|
+
A Node based, visually agnostic implementation of geometric functions, both abstract and finite.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
This library assumes you already have basic knowledge of concepts pertaining to algebra and geometry, including:
|
|
8
|
+
|
|
9
|
+
- **Abstracts**: magnitude, direction, position, coordinate, vector, point, figure, angle
|
|
10
|
+
- **Figures**: line, curve, ellipse, polygon
|
|
11
|
+
- **Transformations**: rotation, translation, reflection, scaling
|
|
12
|
+
- **Units**: radian, degree
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
You may install this library into your JavaScript or TypeScript project using the following command:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install geometric-library
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
You may then `import` any of the classes listed in the **API** section and use them at your own discretion.
|
|
23
|
+
|
|
24
|
+
If you're using TypeScript for your project you can also `import` types and interfaces for all the exported functionalities, which is very much recommended as it will make your life easier in terms of understading how everything works.
|
|
25
|
+
|
|
26
|
+
## API
|
|
27
|
+
|
|
28
|
+
The types and interfaces exported from [./src/types/index.ts](./src/types/index.ts) should give you a pretty good idea of how to use any of the functionalities included in this library.
|
|
29
|
+
|
|
30
|
+
The following classes are available:
|
|
31
|
+
|
|
32
|
+
(Abstracts) `Flag` `Magnitude` `Angle` `Vector` `Point` `Figure`
|
|
33
|
+
|
|
34
|
+
(Figures) `Line` `Polygon` `QuadraticBezierCurve` `CubicBezierCurve` `ArcCurve` `Ellipse` `Circle`
|
|
35
|
+
|
|
36
|
+
## Dependencies
|
|
37
|
+
|
|
38
|
+
JavaScript is bad at math. Don't hate me for saying it, it's actually a pretty well known fact that JavaScript just struggles with algebra, especially when it involves floating point numbers. Here's one of the many articles about it if you want to learn more: [Why JavaScript is Bad At Math](https://javascript.plainenglish.io/why-javascript-is-bad-at-math-9b8247640caa) (by Alexandra Langton).
|
|
39
|
+
|
|
40
|
+
Though there are ways to combat these issues natively, for this library I chose to use an abstraction of [Decimal.js](https://mikemcl.github.io/decimal.js/), which does just that pretty efficiently.
|
|
41
|
+
|
|
42
|
+
As it is an intrinsic dependency so you don't have to install anything yourself.
|
|
43
|
+
|
|
44
|
+
## Contributing
|
|
45
|
+
|
|
46
|
+
This project is open to contribution.
|
|
47
|
+
|
|
48
|
+
If you have a bug to report please open an issue so it can be tracked and addressed as soon as possible. Do include information about how to reproduce the bug, the expected result and the actual result.
|
|
49
|
+
|
|
50
|
+
If there's a missing feature feel free to open an issue as well detailing the feature request and its value to the project and its users.
|
|
51
|
+
|
|
52
|
+
If you already have an idea of how to fix an issue or implement a feature, please also feel free to directly contribute by opening a pull request with the changes. Though the project includes some automated scripts for linting, cleaning, building, testing and deploying the code, please do try to adhere to the existing standards in terms of naming, directory structure and SOLID principles.
|
|
53
|
+
|
|
54
|
+
## TO DO
|
|
55
|
+
|
|
56
|
+
- Add a complete documentation of all the functionalities made available.
|
|
57
|
+
- Add examples.
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
This project is published and distributed under the [MIT License](./LICENSE).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Angle = void 0;
|
|
4
|
+
const Calculator_1 = require("../utilities/Calculator");
|
|
5
|
+
const PI2 = +Calculator_1.Calculator.mul(Math.PI, 2);
|
|
6
|
+
class Angle {
|
|
7
|
+
_radians;
|
|
8
|
+
constructor(value, unit) {
|
|
9
|
+
this._radians = unit === 'radians' ? value : Angle.toRadians(value);
|
|
10
|
+
}
|
|
11
|
+
get cos() {
|
|
12
|
+
return +Calculator_1.Calculator.cos(this.radians);
|
|
13
|
+
}
|
|
14
|
+
get cot() {
|
|
15
|
+
return +Calculator_1.Calculator.div(1, this.tan);
|
|
16
|
+
}
|
|
17
|
+
get degrees() {
|
|
18
|
+
return Angle.toDegrees(this.radians);
|
|
19
|
+
}
|
|
20
|
+
get radians() {
|
|
21
|
+
return this._radians;
|
|
22
|
+
}
|
|
23
|
+
get sin() {
|
|
24
|
+
return +Calculator_1.Calculator.sin(this.radians);
|
|
25
|
+
}
|
|
26
|
+
get tan() {
|
|
27
|
+
return +Calculator_1.Calculator.tan(this.radians);
|
|
28
|
+
}
|
|
29
|
+
static toDegrees(radians) {
|
|
30
|
+
return +Calculator_1.Calculator.mul(radians, 180).div(Math.PI);
|
|
31
|
+
}
|
|
32
|
+
static toRadians(degrees) {
|
|
33
|
+
return +Calculator_1.Calculator.mul(degrees, Math.PI).div(180);
|
|
34
|
+
}
|
|
35
|
+
clone() {
|
|
36
|
+
return new Angle(this.radians, 'radians');
|
|
37
|
+
}
|
|
38
|
+
normalize() {
|
|
39
|
+
const { radians } = this;
|
|
40
|
+
if (radians < 0) {
|
|
41
|
+
this._radians = +Calculator_1.Calculator.add(radians, PI2);
|
|
42
|
+
}
|
|
43
|
+
else if (radians > 0) {
|
|
44
|
+
this._radians = +Calculator_1.Calculator.mod(radians, PI2);
|
|
45
|
+
}
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
replace(value, unit) {
|
|
49
|
+
this._radians = unit === 'radians' ? value : Angle.toRadians(value);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
scale(factor) {
|
|
53
|
+
this._radians = +Calculator_1.Calculator.mul(this.radians, factor);
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
valueOf() {
|
|
57
|
+
return this.radians;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.Angle = Angle;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Figure = void 0;
|
|
4
|
+
const Calculator_1 = require("../utilities/Calculator");
|
|
5
|
+
const Magnitude_1 = require("./Magnitude");
|
|
6
|
+
const Point_1 = require("./Point");
|
|
7
|
+
const Vector_1 = require("./Vector");
|
|
8
|
+
class Figure {
|
|
9
|
+
angles = [];
|
|
10
|
+
isRelative = false;
|
|
11
|
+
magnitudes = [];
|
|
12
|
+
points = [];
|
|
13
|
+
vectors = [];
|
|
14
|
+
_values;
|
|
15
|
+
constructor(values) {
|
|
16
|
+
this.values = this._values = values;
|
|
17
|
+
}
|
|
18
|
+
get boundingBox() {
|
|
19
|
+
const { points } = this;
|
|
20
|
+
return Figure.computeBoundingBox(points);
|
|
21
|
+
}
|
|
22
|
+
get values() {
|
|
23
|
+
return this._values;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @todo Figure out a better way to type narrow than iterating again.
|
|
27
|
+
*/
|
|
28
|
+
set values(values) {
|
|
29
|
+
this._values = values;
|
|
30
|
+
const [points, vectors, magnitudes, angles] = values.reduce((result, value) => {
|
|
31
|
+
if (value instanceof Point_1.Point) {
|
|
32
|
+
result[0].push(value);
|
|
33
|
+
}
|
|
34
|
+
if (value instanceof Vector_1.Vector) {
|
|
35
|
+
result[1].push(value);
|
|
36
|
+
}
|
|
37
|
+
if (value instanceof Magnitude_1.Magnitude) {
|
|
38
|
+
result[2].push(value);
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === 'object' && 'radians' in value) {
|
|
41
|
+
result[3].push(value);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}, [[], [], [], []]);
|
|
45
|
+
this.magnitudes = magnitudes;
|
|
46
|
+
this.points = points;
|
|
47
|
+
this.vectors = vectors;
|
|
48
|
+
this.angles = angles;
|
|
49
|
+
this.isRelative = !!vectors.length;
|
|
50
|
+
}
|
|
51
|
+
static computeBoundingBox(points) {
|
|
52
|
+
if (!points) {
|
|
53
|
+
throw new Error(this.getNoPointsErrorMessage('boundingBox', true));
|
|
54
|
+
}
|
|
55
|
+
const [xValues, yValues] = points.reduce((values, point) => {
|
|
56
|
+
values[0].push(point.x);
|
|
57
|
+
values[1].push(point.y);
|
|
58
|
+
return values;
|
|
59
|
+
}, [[], []]);
|
|
60
|
+
const xMax = +Calculator_1.Calculator.max(xValues);
|
|
61
|
+
const xMin = +Calculator_1.Calculator.min(xValues);
|
|
62
|
+
const yMax = +Calculator_1.Calculator.max(yValues);
|
|
63
|
+
const yMin = +Calculator_1.Calculator.min(yValues);
|
|
64
|
+
return { xMax, xMin, yMax, yMin };
|
|
65
|
+
}
|
|
66
|
+
static getNoPointsErrorMessage(name, property = false) {
|
|
67
|
+
return `Attempting to ${property ? 'get' : 'call'} ${name} of Figure with no points assigned. Assign 'this.points' from the child class and try again.`;
|
|
68
|
+
}
|
|
69
|
+
reflect(about) {
|
|
70
|
+
const { points, vectors } = this;
|
|
71
|
+
if (!points) {
|
|
72
|
+
throw new Error(Figure.getNoPointsErrorMessage('reflect'));
|
|
73
|
+
}
|
|
74
|
+
if ('getPerpendicularProjection' in about) {
|
|
75
|
+
points.forEach((point) => {
|
|
76
|
+
const perpendicularRoot = about.getPerpendicularProjection(point);
|
|
77
|
+
point.reflect(perpendicularRoot);
|
|
78
|
+
});
|
|
79
|
+
vectors.forEach((vector) => {
|
|
80
|
+
vector.reflect({
|
|
81
|
+
x: !about.isHorizontal,
|
|
82
|
+
y: !about.isVertical
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
points.forEach((point) => {
|
|
88
|
+
point.reflect(about);
|
|
89
|
+
});
|
|
90
|
+
vectors.forEach((vector) => {
|
|
91
|
+
vector.reflect({
|
|
92
|
+
x: true,
|
|
93
|
+
y: true
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
rotate(phi, about) {
|
|
99
|
+
const { points, vectors, angles } = this;
|
|
100
|
+
if (!points) {
|
|
101
|
+
throw new Error(Figure.getNoPointsErrorMessage('rotate'));
|
|
102
|
+
}
|
|
103
|
+
points.forEach((point) => {
|
|
104
|
+
point.rotate(phi, about);
|
|
105
|
+
});
|
|
106
|
+
vectors.forEach((vector) => {
|
|
107
|
+
const referencePoint = points[0].clone().rotate(phi, about);
|
|
108
|
+
const positionalPoint = points[0].clone().translate(vector).rotate(phi, about);
|
|
109
|
+
const rotatedVector = new Vector_1.Vector([referencePoint, positionalPoint]);
|
|
110
|
+
vector.replace(rotatedVector);
|
|
111
|
+
});
|
|
112
|
+
angles.forEach((angle) => {
|
|
113
|
+
angle.replace(+Calculator_1.Calculator.add(+angle, +phi), 'radians').normalize();
|
|
114
|
+
});
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
scale(factor, about = new Point_1.Point([0, 0])) {
|
|
118
|
+
const { points, vectors, magnitudes } = this;
|
|
119
|
+
if (!points) {
|
|
120
|
+
throw new Error(Figure.getNoPointsErrorMessage('scale'));
|
|
121
|
+
}
|
|
122
|
+
points.forEach((point) => {
|
|
123
|
+
const positionalVector = new Vector_1.Vector([about, point]);
|
|
124
|
+
const scaledPositionalVector = positionalVector.clone().scale(factor);
|
|
125
|
+
const scaledPoint = about.clone().translate(scaledPositionalVector);
|
|
126
|
+
point.replace(scaledPoint);
|
|
127
|
+
});
|
|
128
|
+
vectors.forEach((vector) => {
|
|
129
|
+
vector.scale(factor);
|
|
130
|
+
});
|
|
131
|
+
magnitudes.forEach((magnitude) => {
|
|
132
|
+
magnitude.scale(factor);
|
|
133
|
+
});
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
translate(vector) {
|
|
137
|
+
const { points } = this;
|
|
138
|
+
if (!points) {
|
|
139
|
+
throw new Error(Figure.getNoPointsErrorMessage('translate'));
|
|
140
|
+
}
|
|
141
|
+
points.forEach((point) => {
|
|
142
|
+
point.translate(vector);
|
|
143
|
+
});
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.Figure = Figure;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Flag = void 0;
|
|
4
|
+
class Flag {
|
|
5
|
+
_value;
|
|
6
|
+
constructor(value) {
|
|
7
|
+
this._value = value;
|
|
8
|
+
}
|
|
9
|
+
get value() {
|
|
10
|
+
return this._value;
|
|
11
|
+
}
|
|
12
|
+
clone() {
|
|
13
|
+
return new Flag(this._value);
|
|
14
|
+
}
|
|
15
|
+
invert() {
|
|
16
|
+
this._value = !this._value;
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
replace(value) {
|
|
20
|
+
this._value = value;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
valueOf() {
|
|
24
|
+
return +this._value;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.Flag = Flag;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Magnitude = void 0;
|
|
4
|
+
const Calculator_1 = require("../utilities/Calculator");
|
|
5
|
+
class Magnitude {
|
|
6
|
+
_value;
|
|
7
|
+
constructor(value) {
|
|
8
|
+
this._value = value;
|
|
9
|
+
}
|
|
10
|
+
clone() {
|
|
11
|
+
return new Magnitude(+this);
|
|
12
|
+
}
|
|
13
|
+
replace(value) {
|
|
14
|
+
this._value = value;
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
scale(factor) {
|
|
18
|
+
this._value = +Calculator_1.Calculator.mul(+this, factor);
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
valueOf() {
|
|
22
|
+
return this._value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.Magnitude = Magnitude;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Point = void 0;
|
|
4
|
+
const Calculator_1 = require("../utilities/Calculator");
|
|
5
|
+
class Point {
|
|
6
|
+
_x;
|
|
7
|
+
_y;
|
|
8
|
+
constructor([x, y]) {
|
|
9
|
+
this._x = x;
|
|
10
|
+
this._y = y;
|
|
11
|
+
}
|
|
12
|
+
get values() {
|
|
13
|
+
return [this.x, this.y];
|
|
14
|
+
}
|
|
15
|
+
get x() {
|
|
16
|
+
return +this._x;
|
|
17
|
+
}
|
|
18
|
+
get y() {
|
|
19
|
+
return +this._y;
|
|
20
|
+
}
|
|
21
|
+
clone() {
|
|
22
|
+
const { x, y } = this;
|
|
23
|
+
return new Point([x, y]);
|
|
24
|
+
}
|
|
25
|
+
reflect(about) {
|
|
26
|
+
const { x, y } = this;
|
|
27
|
+
this._x = +Calculator_1.Calculator.sub(about.x, x).mul(2).add(x);
|
|
28
|
+
this._y = +Calculator_1.Calculator.sub(about.y, y).mul(2).add(y);
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
replace(point) {
|
|
32
|
+
this._x = point.x;
|
|
33
|
+
this._y = point.y;
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
rotate(phi, about = new Point([0, 0])) {
|
|
37
|
+
const dx = Calculator_1.Calculator.sub(this.x, about.x);
|
|
38
|
+
const dy = Calculator_1.Calculator.sub(this.y, about.y);
|
|
39
|
+
this._x = +dx.mul(phi.cos).sub(dy.mul(phi.sin)).add(about.x);
|
|
40
|
+
this._y = +dy.mul(phi.cos).add(dx.mul(phi.sin)).add(about.y);
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
translate(vector) {
|
|
44
|
+
const { x, y } = this;
|
|
45
|
+
this._x = +Calculator_1.Calculator.add(x, vector.dx);
|
|
46
|
+
this._y = +Calculator_1.Calculator.add(y, vector.dy);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.Point = Point;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Vector = void 0;
|
|
4
|
+
const Calculator_1 = require("../utilities/Calculator");
|
|
5
|
+
const Angle_1 = require("./Angle");
|
|
6
|
+
class Vector {
|
|
7
|
+
_dx;
|
|
8
|
+
_dy;
|
|
9
|
+
constructor(values) {
|
|
10
|
+
const fromPoints = values.some((value) => typeof value !== 'number');
|
|
11
|
+
if (fromPoints) {
|
|
12
|
+
const [P1, P2] = values;
|
|
13
|
+
this._dx = +Calculator_1.Calculator.sub(P2.x, P1.x);
|
|
14
|
+
this._dy = +Calculator_1.Calculator.sub(P2.y, P1.y);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const [dx, dy] = values;
|
|
18
|
+
this._dx = dx;
|
|
19
|
+
this._dy = dy;
|
|
20
|
+
}
|
|
21
|
+
get dx() {
|
|
22
|
+
return this._dx;
|
|
23
|
+
}
|
|
24
|
+
get dy() {
|
|
25
|
+
return this._dy;
|
|
26
|
+
}
|
|
27
|
+
get magnitude() {
|
|
28
|
+
const { dx, dy } = this;
|
|
29
|
+
return +Calculator_1.Calculator.pow(dx, 2).add(Calculator_1.Calculator.pow(dy, 2)).sqrt();
|
|
30
|
+
}
|
|
31
|
+
get values() {
|
|
32
|
+
return [this.dx, this.dy];
|
|
33
|
+
}
|
|
34
|
+
angleTo(vector) {
|
|
35
|
+
const dotProd = this.dotProduct(vector);
|
|
36
|
+
const magV1 = this.magnitude;
|
|
37
|
+
const magV2 = vector.magnitude;
|
|
38
|
+
const magProd = Calculator_1.Calculator.mul(magV1, magV2);
|
|
39
|
+
const cosAngle = Calculator_1.Calculator.div(dotProd, magProd);
|
|
40
|
+
const angle = cosAngle.acos();
|
|
41
|
+
return new Angle_1.Angle(+angle, 'radians');
|
|
42
|
+
}
|
|
43
|
+
clone() {
|
|
44
|
+
const { dx, dy } = this;
|
|
45
|
+
return new Vector([dx, dy]);
|
|
46
|
+
}
|
|
47
|
+
dotProduct(vector) {
|
|
48
|
+
const { dx, dy } = this;
|
|
49
|
+
return +Calculator_1.Calculator.mul(dx, vector.dx).add(Calculator_1.Calculator.mul(dy, vector.dy));
|
|
50
|
+
}
|
|
51
|
+
reflect({ x, y }) {
|
|
52
|
+
this._dx = x ? +Calculator_1.Calculator.neg(this.dx) : this.dx;
|
|
53
|
+
this._dy = y ? +Calculator_1.Calculator.neg(this.dy) : this.dy;
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
replace(vector) {
|
|
57
|
+
this._dx = vector.dx;
|
|
58
|
+
this._dy = vector.dy;
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
rotate(phi) {
|
|
62
|
+
const { dx, dy } = this;
|
|
63
|
+
this._dx = +Calculator_1.Calculator.mul(phi.cos, dx).sub(Calculator_1.Calculator.mul(phi.sin, dy));
|
|
64
|
+
this._dy = +Calculator_1.Calculator.mul(phi.sin, dx).add(Calculator_1.Calculator.mul(phi.cos, dy));
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
scale(factor) {
|
|
68
|
+
const { dx, dy } = this;
|
|
69
|
+
this._dx = +Calculator_1.Calculator.mul(dx, factor);
|
|
70
|
+
this._dy = +Calculator_1.Calculator.mul(dy, factor);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.Vector = Vector;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ArcCurve = void 0;
|
|
4
|
+
const Calculator_1 = require("../utilities/Calculator");
|
|
5
|
+
const Point_1 = require("../abstracts/Point");
|
|
6
|
+
const Figure_1 = require("../abstracts/Figure");
|
|
7
|
+
const Ellipse_1 = require("./Ellipse");
|
|
8
|
+
class ArcCurve extends Figure_1.Figure {
|
|
9
|
+
ellipse;
|
|
10
|
+
largeArcFlag;
|
|
11
|
+
phi;
|
|
12
|
+
rx;
|
|
13
|
+
ry;
|
|
14
|
+
sweepFlag;
|
|
15
|
+
constructor(values) {
|
|
16
|
+
super(values);
|
|
17
|
+
const [, rx, ry, phi, largeArcFlag, sweepFlag] = values;
|
|
18
|
+
this.rx = rx;
|
|
19
|
+
this.ry = ry;
|
|
20
|
+
this.phi = phi;
|
|
21
|
+
this.largeArcFlag = largeArcFlag;
|
|
22
|
+
this.sweepFlag = sweepFlag;
|
|
23
|
+
this.adjustRadii();
|
|
24
|
+
const center = this.computeCenter();
|
|
25
|
+
this.ellipse = new Ellipse_1.Ellipse([center, rx, ry, phi]);
|
|
26
|
+
}
|
|
27
|
+
get boundingBox() {
|
|
28
|
+
const { P0, P1 } = this;
|
|
29
|
+
const points = [P0, P1, ...this.criticalPoints];
|
|
30
|
+
return Figure_1.Figure.computeBoundingBox(points);
|
|
31
|
+
}
|
|
32
|
+
get center() {
|
|
33
|
+
return this.ellipse.center;
|
|
34
|
+
}
|
|
35
|
+
get criticalPoints() {
|
|
36
|
+
const { ellipse } = this;
|
|
37
|
+
const [minTheta, maxTheta] = this.computeThetaRange();
|
|
38
|
+
return ellipse.criticalPoints.filter((point) => {
|
|
39
|
+
const { P0, P1 } = this;
|
|
40
|
+
if ((point.x === P0.x && point.y === P0.y) || (point.x === P1.x && point.y === P1.y)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const theta = ellipse.computeThetaForPoint(point);
|
|
44
|
+
return +theta >= +minTheta && +theta <= +maxTheta;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
get P0() {
|
|
48
|
+
return this.points[0];
|
|
49
|
+
}
|
|
50
|
+
get P1() {
|
|
51
|
+
if (this.isRelative) {
|
|
52
|
+
return this.P0.clone().translate(this.vectors[0]);
|
|
53
|
+
}
|
|
54
|
+
return this.points[1];
|
|
55
|
+
}
|
|
56
|
+
clone() {
|
|
57
|
+
const values = this.values.map((value) => (typeof value === 'object' && 'clone' in value ? value.clone() : value));
|
|
58
|
+
return new ArcCurve(values);
|
|
59
|
+
}
|
|
60
|
+
reflect(about) {
|
|
61
|
+
super.reflect(about);
|
|
62
|
+
this.sweepFlag.invert();
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
adjustRadii() {
|
|
66
|
+
const { rx, ry } = this;
|
|
67
|
+
const P0_prime = this.computeP0Prime();
|
|
68
|
+
const { x: x1_prime, y: y1_prime } = P0_prime;
|
|
69
|
+
const rx_sq = Calculator_1.Calculator.pow(+rx, 2);
|
|
70
|
+
const ry_sq = Calculator_1.Calculator.pow(+ry, 2);
|
|
71
|
+
const x1_prime_sq = Calculator_1.Calculator.pow(x1_prime, 2);
|
|
72
|
+
const y1_prime_sq = Calculator_1.Calculator.pow(y1_prime, 2);
|
|
73
|
+
const radii_check = x1_prime_sq.div(rx_sq).add(y1_prime_sq.div(ry_sq));
|
|
74
|
+
if (+radii_check > 1) {
|
|
75
|
+
rx.replace(+Calculator_1.Calculator.mul(+rx, radii_check.sqrt()));
|
|
76
|
+
ry.replace(+Calculator_1.Calculator.mul(+ry, radii_check.sqrt()));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
computeCenter() {
|
|
80
|
+
const { P0, P1, phi } = this;
|
|
81
|
+
const center_prime = this.computeCenterPrime();
|
|
82
|
+
const { x: x1, y: y1 } = P0;
|
|
83
|
+
const { x: x2, y: y2 } = P1;
|
|
84
|
+
const { x: cx_prime, y: cy_prime } = center_prime;
|
|
85
|
+
const dx = Calculator_1.Calculator.add(x1, x2).div(2);
|
|
86
|
+
const dy = Calculator_1.Calculator.add(y1, y2).div(2);
|
|
87
|
+
const cx = Calculator_1.Calculator.mul(phi.cos, cx_prime).sub(Calculator_1.Calculator.mul(phi.sin, cy_prime)).add(dx);
|
|
88
|
+
const cy = Calculator_1.Calculator.mul(phi.sin, cx_prime).add(Calculator_1.Calculator.mul(phi.cos, cy_prime)).add(dy);
|
|
89
|
+
return new Point_1.Point([+cx, +cy]);
|
|
90
|
+
}
|
|
91
|
+
computeCenterPrime() {
|
|
92
|
+
const { largeArcFlag, sweepFlag, rx, ry } = this;
|
|
93
|
+
const P0_prime = this.computeP0Prime();
|
|
94
|
+
const { x: x1_prime, y: y1_prime } = P0_prime;
|
|
95
|
+
const rx_sq = Calculator_1.Calculator.pow(+rx, 2);
|
|
96
|
+
const ry_sq = Calculator_1.Calculator.pow(+ry, 2);
|
|
97
|
+
const x1_prime_sq = Calculator_1.Calculator.pow(x1_prime, 2);
|
|
98
|
+
const y1_prime_sq = Calculator_1.Calculator.pow(y1_prime, 2);
|
|
99
|
+
const sign = new Calculator_1.Calculator(+largeArcFlag === +sweepFlag ? -1 : 1);
|
|
100
|
+
let sq = rx_sq
|
|
101
|
+
.mul(ry_sq)
|
|
102
|
+
.sub(rx_sq.mul(y1_prime_sq))
|
|
103
|
+
.sub(ry_sq.mul(x1_prime_sq))
|
|
104
|
+
.div(rx_sq.mul(y1_prime_sq).add(ry_sq.mul(x1_prime_sq)));
|
|
105
|
+
sq = +sq < 0 ? new Calculator_1.Calculator(0) : sq;
|
|
106
|
+
const coef = sign.mul(sq.sqrt());
|
|
107
|
+
const cx_prime = coef.mul(Calculator_1.Calculator.mul(+rx, y1_prime).div(+ry));
|
|
108
|
+
const cy_prime = coef.mul(Calculator_1.Calculator.mul(+ry, x1_prime).div(+rx).neg());
|
|
109
|
+
return new Point_1.Point([+cx_prime, +cy_prime]);
|
|
110
|
+
}
|
|
111
|
+
computeP0Prime() {
|
|
112
|
+
const { P0, P1, phi } = this;
|
|
113
|
+
const { x: x1, y: y1 } = P0;
|
|
114
|
+
const { x: x2, y: y2 } = P1;
|
|
115
|
+
const mx = Calculator_1.Calculator.sub(x1, x2).div(2);
|
|
116
|
+
const my = Calculator_1.Calculator.sub(y1, y2).div(2);
|
|
117
|
+
const x1_prime = Calculator_1.Calculator.mul(phi.cos, mx).add(Calculator_1.Calculator.mul(phi.sin, my));
|
|
118
|
+
const y1_prime = Calculator_1.Calculator.mul(phi.sin, mx).neg().add(Calculator_1.Calculator.mul(phi.cos, my));
|
|
119
|
+
return new Point_1.Point([+x1_prime, +y1_prime]);
|
|
120
|
+
}
|
|
121
|
+
computeThetaRange() {
|
|
122
|
+
const { P0, P1, ellipse, sweepFlag } = this;
|
|
123
|
+
const theta1 = ellipse.computeThetaForPoint(P0);
|
|
124
|
+
const theta2 = ellipse.computeThetaForPoint(P1);
|
|
125
|
+
if (+theta2 === 0 && !sweepFlag.value) {
|
|
126
|
+
theta2.replace(360, 'degrees');
|
|
127
|
+
}
|
|
128
|
+
const thetaRange = [theta1, theta2].sort((a, b) => +Calculator_1.Calculator.sub(a.radians, b.radians));
|
|
129
|
+
return thetaRange;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
exports.ArcCurve = ArcCurve;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Circle = void 0;
|
|
4
|
+
const Calculator_1 = require("../utilities/Calculator");
|
|
5
|
+
const Ellipse_1 = require("./Ellipse");
|
|
6
|
+
const Point_1 = require("../abstracts/Point");
|
|
7
|
+
const Angle_1 = require("../abstracts/Angle");
|
|
8
|
+
class Circle extends Ellipse_1.Ellipse {
|
|
9
|
+
_radius;
|
|
10
|
+
constructor(values) {
|
|
11
|
+
const [center, radius] = values;
|
|
12
|
+
const phi = new Angle_1.Angle(0, 'radians'); // Always 0 since circles don't change with rotation.
|
|
13
|
+
super([center, radius, radius.clone(), phi]);
|
|
14
|
+
this._radius = radius;
|
|
15
|
+
}
|
|
16
|
+
get isCircle() {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
get radius() {
|
|
20
|
+
return this._radius;
|
|
21
|
+
}
|
|
22
|
+
get values() {
|
|
23
|
+
const [center, radius] = super.values;
|
|
24
|
+
return [center, radius];
|
|
25
|
+
}
|
|
26
|
+
set values(values) {
|
|
27
|
+
super.values = values;
|
|
28
|
+
}
|
|
29
|
+
clone() {
|
|
30
|
+
const values = this.values.map((value) => value.clone());
|
|
31
|
+
return new Circle(values);
|
|
32
|
+
}
|
|
33
|
+
computeCriticalPoints(ellipse = this) {
|
|
34
|
+
const { center, rx } = ellipse;
|
|
35
|
+
const radius = rx;
|
|
36
|
+
const first = new Point_1.Point([center.x, +Calculator_1.Calculator.add(center.y, +radius)]);
|
|
37
|
+
const second = new Point_1.Point([+Calculator_1.Calculator.add(center.x, +radius), center.y]);
|
|
38
|
+
const third = new Point_1.Point([center.x, +Calculator_1.Calculator.sub(center.y, +radius)]);
|
|
39
|
+
const fourth = new Point_1.Point([+Calculator_1.Calculator.sub(center.x, +radius), center.y]);
|
|
40
|
+
return [first, second, third, fourth];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.Circle = Circle;
|